/**************************************************************************/ /* */ /* Copyright (c) Microsoft Corporation. All rights reserved. */ /* */ /* This software is licensed under the Microsoft Software License */ /* Terms for Microsoft Azure RTOS. Full text of the 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 #include #include #pragma comment (lib, "Winmm.lib") /* Define various Win32 objects used by the ThreadX port. */ TX_WIN32_CRITICAL_SECTION _tx_win32_critical_section; HANDLE _tx_win32_scheduler_semaphore; DWORD _tx_win32_scheduler_id; ULONG _tx_win32_global_int_disabled_flag; LARGE_INTEGER _tx_win32_time_stamp; ULONG _tx_win32_system_error; extern TX_THREAD *_tx_thread_current_ptr; /* Define simulated timer interrupt. This is done inside a thread, which is how other interrupts may be defined as well. See code below for an example. */ UINT _tx_win32_timer_id; VOID CALLBACK _tx_win32_timer_interrupt(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2); #ifdef TX_WIN32_DEBUG_ENABLE extern ULONG _tx_thread_system_state; extern UINT _tx_thread_preempt_disable; extern TX_THREAD *_tx_thread_current_ptr; extern TX_THREAD *_tx_thread_execute_ptr; /* Define the maximum size of the Win32 debug array. */ #ifndef TX_WIN32_DEBUG_EVENT_SIZE #define TX_WIN32_DEBUG_EVENT_SIZE 400 #endif /* Define debug log in order to debug Win32 issues with this port. */ typedef struct TX_WIN32_DEBUG_ENTRY_STRUCT { char *tx_win32_debug_entry_action; LARGE_INTEGER tx_win32_debug_entry_timestamp; char *tx_win32_debug_entry_file; unsigned long tx_win32_debug_entry_line; TX_WIN32_CRITICAL_SECTION tx_win32_debug_entry_critical_section; unsigned long tx_win32_debug_entry_int_disabled_flag; ULONG tx_win32_debug_entry_system_state; UINT tx_win32_debug_entry_preempt_disable; TX_THREAD *tx_win32_debug_entry_current_thread; DWORD tx_win32_debug_entry_current_thread_id; TX_THREAD *tx_win32_debug_entry_execute_thread; DWORD tx_win32_debug_entry_execute_thread_id; DWORD tx_win32_debug_entry_running_id; } TX_WIN32_DEBUG_ENTRY; /* Define the circular array of Win32 debug entries. */ TX_WIN32_DEBUG_ENTRY _tx_win32_debug_entry_array[TX_WIN32_DEBUG_EVENT_SIZE]; /* Define the Win32 debug index. */ unsigned long _tx_win32_debug_entry_index = 0; /* Now define the debug entry function. */ void _tx_win32_debug_entry_insert(char *action, char *file, unsigned long line) { /* Get the time stamp. */ QueryPerformanceCounter((LARGE_INTEGER *)&_tx_win32_time_stamp); /* Setup the debug entry. */ _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_action = action; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_timestamp = _tx_win32_time_stamp; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_file = file; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_line = line; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_critical_section = _tx_win32_critical_section; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_int_disabled_flag = _tx_win32_global_int_disabled_flag; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_system_state = _tx_thread_system_state; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_preempt_disable = _tx_thread_preempt_disable; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread = _tx_thread_current_ptr; if (_tx_thread_current_ptr) _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread_id = _tx_thread_current_ptr -> tx_thread_win32_thread_id; else _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_current_thread_id = 0; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread = _tx_thread_execute_ptr; if (_tx_thread_execute_ptr) _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread_id = _tx_thread_execute_ptr -> tx_thread_win32_thread_id; else _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_execute_thread_id = 0; _tx_win32_debug_entry_array[_tx_win32_debug_entry_index].tx_win32_debug_entry_running_id = GetCurrentThreadId(); /* Now move to the next entry. */ _tx_win32_debug_entry_index++; /* Determine if we need to wrap the list. */ if (_tx_win32_debug_entry_index >= TX_WIN32_DEBUG_EVENT_SIZE) { /* Yes, wrap the list! */ _tx_win32_debug_entry_index = 0; } } #endif /* Define the ThreadX timer interrupt handler. */ void _tx_timer_interrupt(void); /* Define other external function references. */ VOID _tx_initialize_low_level(VOID); VOID _tx_thread_context_save(VOID); VOID _tx_thread_context_restore(VOID); /* Define other external variable references. */ extern VOID *_tx_initialize_unused_memory; /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _tx_initialize_low_level Win32/Visual */ /* 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 */ /* */ /* CreateMutex Win32 create mutex */ /* CreateThread Win32 create thread */ /* CreateSemaphore Win32 create semaphore */ /* GetCurrentThreadId Win32 get current thread ID */ /* SetProcessAffinityMask Win32 process affinity set */ /* SetThreadPriority Win32 set thread priority */ /* */ /* 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) { /* Deprecate TX_WIN32_MULTI_CORE build option and default to restricting execution to one core. */ #ifndef TX_WIN32_BYPASS_AFFINITY_SETUP /* Limit this ThreadX simulation on Win32 to a single core. */ if (SetProcessAffinityMask( GetCurrentProcess(), 1 ) == 0) { /* Error restricting the process to one core. */ printf("ThreadX Win32 error restricting the process to one core!\n"); while(1) { } } #endif /* Pickup the first available memory address. */ /* Save the first available memory address. */ _tx_initialize_unused_memory = malloc(TX_WIN32_MEMORY_SIZE); /* Pickup the unique Id of the current thread, which will also be the Id of the scheduler. */ _tx_win32_scheduler_id = GetCurrentThreadId(); /* Create the system critical section mutex. This is used by the system to block all other access, analogous to an interrupt lockout on an embedded target. */ _tx_win32_critical_section.tx_win32_critical_section_mutex_handle = CreateMutex(NULL, FALSE, NULL); _tx_win32_critical_section.tx_win32_critical_section_nested_count = 0; _tx_win32_critical_section.tx_win32_critical_section_owner = 0; /* Create the semaphore that regulates when the scheduler executes. */ _tx_win32_scheduler_semaphore = CreateSemaphore(NULL, 0, 1, NULL); /* Initialize the global interrupt disabled flag. */ _tx_win32_global_int_disabled_flag = TX_FALSE; /* Done, return to caller. */ } /* This routine is called after initialization is complete in order to start all interrupt threads. Interrupt threads in addition to the timer may be added to this routine as well. */ void _tx_initialize_start_interrupts(void) { TIMECAPS tc; UINT wTimerRes; /* Queries the timer device to determine its resolution. */ if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { /* Error; application can't continue. */ printf("Query timer device error."); while (1) { } } wTimerRes = min(max(tc.wPeriodMin, TX_TIMER_PERIODIC), tc.wPeriodMax); /* Start a specified timer event. The timer runs in its own thread. It calls the specified callback function when the event is activated. */ _tx_win32_timer_id = timeSetEvent(TX_TIMER_PERIODIC, wTimerRes, _tx_win32_timer_interrupt, 0, TIME_PERIODIC); } /* Define the ThreadX system timer interrupt. Other interrupts may be simulated in a similar way. */ VOID CALLBACK _tx_win32_timer_interrupt(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2) { /* Call ThreadX context save for interrupt preparation. */ _tx_thread_context_save(); /* Call the ThreadX system timer interrupt processing. */ _tx_timer_interrupt(); /* Call ThreadX context restore for interrupt completion. */ _tx_thread_context_restore(); }