xref: /nrf52832-nimble/rt-thread/libcpu/sim/win32/cpu_port.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
1 /*
2 ************************************************************************************************************************
3 * File    : cpu_port.c
4 * By      : xyou
5 * Version : V1.00.00
6 *
7 * By      : prife
8 * Version : V1.00.01
9 ************************************************************************************************************************
10 */
11 
12 /*
13 *********************************************************************************************************
14 *                                             INCLUDE FILES
15 *********************************************************************************************************
16 */
17 #include  <rtthread.h>
18 #include  <windows.h>
19 #include  <mmsystem.h>
20 #include  <stdio.h>
21 #include  "cpu_port.h"
22 
23 /*
24 *********************************************************************************************************
25 *                                             WinThread STRUCTURE
26 *  Windows runs each task in a thread.
27 *  The context switch is managed by the threads.So the task stack does not have to be managed directly,
28 *  although the stack stack is still used to hold an WinThreadState structure this is the only thing it
29 *  will be ever hold.
30 *  the structure indirectly maps the task handle to a thread handle
31 *********************************************************************************************************
32 */
33 typedef struct
34 {
35     void            *Param;                     //Thread param
36     void            (*Entry)(void *);           //Thread entry
37     void            (*Exit)(void);                      //Thread exit
38     HANDLE          ThreadHandle;
39     DWORD           ThreadID;
40 }win_thread_t;
41 
42 const DWORD MS_VC_EXCEPTION=0x406D1388;
43 
44 #pragma pack(push,8)
45 typedef struct tagTHREADNAME_INFO
46 {
47 	DWORD dwType; // Must be 0x1000.
48 	LPCSTR szName; // Pointer to name (in user addr space).
49 	DWORD dwThreadID; // Thread ID (-1=caller thread).
50 	DWORD dwFlags; // Reserved for future use, must be zero.
51 } THREADNAME_INFO;
52 #pragma pack(pop)
53 
54 /*
55 *********************************************************************************************************
56 *                                             LOCAL DEFINES
57 *********************************************************************************************************
58 */
59 #define MAX_INTERRUPT_NUM       ((rt_uint32_t)sizeof(rt_uint32_t) * 8)
60 
61 /*
62  * Simulated interrupt waiting to be processed.this is a bit mask where each bit represent one interrupt
63  * so a maximum of 32 interrupts can be simulated
64  */
65 static volatile rt_uint32_t  CpuPendingInterrupts = 0;
66 
67 /*
68  * An event used to inform the simulated interrupt processing thread (a high priority thread
69  *      that simulated interrupt processing) that an interrupt is pending
70  */
71 static HANDLE   hInterruptEventHandle = NULL;
72 
73 /*
74  * Mutex used to protect all the simulated interrupt variables that are accessed by multiple threads
75  */
76 static HANDLE   hInterruptEventMutex = NULL;
77 
78 /*
79  * Handler for all the simulate software interrupts.
80  * The first two positions are used the Yield and Tick interrupt so are handled slightly differently
81  * all the other interrupts can be user defined
82 */
83 static rt_uint32_t (*CpuIsrHandler[MAX_INTERRUPT_NUM])(void) = {0};
84 
85 /*
86  * Handler for OSTick Thread
87  */
88 static HANDLE       OSTick_Thread;
89 static DWORD        OSTick_ThreadID;
90 static HANDLE       OSTick_SignalPtr;
91 static TIMECAPS     OSTick_TimerCap;
92 static MMRESULT     OSTick_TimerID;
93 
94 /*
95  * flag in interrupt handling
96  */
97 rt_uint32_t rt_interrupt_from_thread, rt_interrupt_to_thread;
98 rt_uint32_t rt_thread_switch_interrupt_flag;
99 
100 /*
101 *********************************************************************************************************
102 *                                             PRIVATE FUNCTION PROTOTYPES
103 *********************************************************************************************************
104 */
105 //static void WinThreadScheduler(void);
106 void WinThreadScheduler(void);
107 rt_uint32_t YieldInterruptHandle(void);
108 rt_uint32_t SysTickInterruptHandle(void);
109 static DWORD WINAPI ThreadforSysTickTimer(LPVOID lpParam);
110 static DWORD WINAPI ThreadforKeyGet(LPVOID lpParam);
111 
112 static void SetThreadName(DWORD dwThreadID, char* threadName)
113 {
114 #if defined(_MSC_VER)
115 	THREADNAME_INFO info;
116 	info.dwType = 0x1000;
117 	info.szName = threadName;
118 	info.dwThreadID = dwThreadID;
119 	info.dwFlags = 0;
120 
121 	__try
122 	{
123 		RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
124 	}
125 	__except(EXCEPTION_EXECUTE_HANDLER)
126 	{
127 	}
128 #endif
129 }
130 
131 /*
132 *********************************************************************************************************
133 *                                            rt_hw_stack_init()
134 * Description : Initialize stack of thread
135 * Argument(s) : void *pvEntry,void *pvParam,rt_uint8_t *pStackAddr,void *pvExit
136 * Return(s)   : rt_uint8_t*
137 * Caller(s)   : rt_thread_init or rt_thread_create
138 * Note(s)     : none
139 *********************************************************************************************************
140 */
141 
142 static DWORD WINAPI thread_run( LPVOID lpThreadParameter )
143 {
144 	rt_thread_t tid = rt_thread_self();
145 	win_thread_t  *pWinThread = (win_thread_t *)lpThreadParameter;
146 
147 	SetThreadName(GetCurrentThreadId(), tid->name);
148 
149 	pWinThread->Entry(pWinThread->Param);
150 
151 	pWinThread->Exit();
152 	return 0;
153 }
154 
155 rt_uint8_t* rt_hw_stack_init(void *pEntry,void *pParam,rt_uint8_t *pStackAddr,void *pExit)
156 {
157     win_thread_t    *pWinThread = NULL;
158 
159     /*
160      * In this simulated case a stack is not initialized
161      * The thread handles the context switching itself. The WinThreadState object is placed onto the stack
162      * that was created for the task
163      * so the stack buffer is still used,just not in the conventional way.
164      */
165     pWinThread = (win_thread_t *)(pStackAddr - sizeof(win_thread_t));
166 
167     pWinThread->Entry = pEntry;
168     pWinThread->Param = pParam;
169     pWinThread->Exit = pExit;
170 
171     pWinThread->ThreadHandle = NULL;
172     pWinThread->ThreadID = 0;
173 
174     /* Create the winthread */
175     pWinThread->ThreadHandle = CreateThread(NULL,
176                                             0,
177                                             (LPTHREAD_START_ROUTINE) thread_run,
178                                             pWinThread,
179                                             CREATE_SUSPENDED,
180                                             &(pWinThread->ThreadID));
181     SetThreadAffinityMask(pWinThread->ThreadHandle,
182                           0x01);
183     SetThreadPriorityBoost(pWinThread->ThreadHandle,
184                            TRUE);
185     SetThreadPriority(pWinThread->ThreadHandle,
186                       THREAD_PRIORITY_IDLE);
187 
188     return (rt_uint8_t*)pWinThread;
189 } /*** rt_hw_stack_init ***/
190 
191 /*
192 *********************************************************************************************************
193 *                                            rt_hw_interrupt_disable()
194 * Description : disable cpu interrupts
195 * Argument(s) : void
196 * Return(s)   : rt_base_t
197 * Caller(s)   : Applicatios or os_kernel
198 * Note(s)     : none
199 *********************************************************************************************************
200 */
201 rt_base_t rt_hw_interrupt_disable(void)
202 {
203     if(hInterruptEventMutex != NULL)
204     {
205         WaitForSingleObject(hInterruptEventMutex,INFINITE);
206     }
207 
208     return 0;
209 } /*** rt_hw_interrupt_disable ***/
210 
211 
212 /*
213 *********************************************************************************************************
214 *                                            rt_hw_interrupt_enable()
215 * Description : enable cpu interrupts
216 * Argument(s) : rt_base_t level
217 * Return(s)   : void
218 * Caller(s)   : Applications or os_kernel
219 * Note(s)     : none
220 *********************************************************************************************************
221 */
222 void rt_hw_interrupt_enable(rt_base_t level)
223 {
224     level = level;
225 
226     if (hInterruptEventMutex != NULL)
227     {
228         ReleaseMutex(hInterruptEventMutex);
229     }
230 
231 } /*** rt_hw_interrupt_enable ***/
232 
233 /*
234 *********************************************************************************************************
235 *                                            rt_hw_context_switch_interrupt()
236 * Description : switch thread's contex
237 * Argument(s) : void
238 * Return(s)   : void
239 * Caller(s)   : os kernel
240 * Note(s)     : none
241 *********************************************************************************************************
242 */
243 void rt_hw_context_switch_interrupt(rt_uint32_t from,
244                                     rt_uint32_t to)
245 {
246     if(rt_thread_switch_interrupt_flag != 1)
247     {
248         rt_thread_switch_interrupt_flag = 1;
249 
250         // set rt_interrupt_from_thread
251         rt_interrupt_from_thread = *((rt_uint32_t *)(from));
252     }
253 
254     rt_interrupt_to_thread = *((rt_uint32_t *)(to));
255 
256 	//trigger YIELD exception(cause context switch)
257     TriggerSimulateInterrupt(CPU_INTERRUPT_YIELD);
258 } /*** rt_hw_context_switch_interrupt ***/
259 
260 
261 
262 void rt_hw_context_switch(rt_uint32_t from,
263                           rt_uint32_t to)
264 {
265     if(rt_thread_switch_interrupt_flag != 1)
266     {
267         rt_thread_switch_interrupt_flag  = 1;
268 
269         // set rt_interrupt_from_thread
270         rt_interrupt_from_thread = *((rt_uint32_t *)(from));
271 
272     }
273 
274     // set rt_interrupt_to_thread
275     rt_interrupt_to_thread = *((rt_uint32_t *)(to));
276 
277     //trigger YIELD exception(cause contex switch)
278     TriggerSimulateInterrupt(CPU_INTERRUPT_YIELD);
279 
280 } /*** rt_hw_context_switch ***/
281 
282 /*
283 *********************************************************************************************************
284 *                                            rt_hw_context_switch_to()
285 * Description : switch to new thread
286 * Argument(s) : rt_uint32_t to              //the stack address of the thread which will switch to
287 * Return(s)   : void
288 * Caller(s)   : rt_thread schecale
289 * Note(s)     : this function is used to perform the first thread switch
290 *********************************************************************************************************
291 */
292 void rt_hw_context_switch_to(rt_uint32_t to)
293 {
294 	//set to thread
295     rt_interrupt_to_thread = *((rt_uint32_t *)(to));
296 
297     //clear from thread
298     rt_interrupt_from_thread = 0;
299 
300     //set interrupt to 1
301     rt_thread_switch_interrupt_flag = 1;
302 
303     //start WinThreadScheduler
304     WinThreadScheduler();
305 
306     //never reach here!
307     return;
308 
309 } /*** rt_hw_context_switch_to ***/
310 
311 
312 
313 /*
314 *********************************************************************************************************
315 *                                            TriggerSimulateInterrupt()
316 * Description : Trigger a simulated interrupts handle
317 * Argument(s) : t_uint32_t IntIndex
318 * Return(s)   : void
319 * Caller(s)   : Applications
320 * Note(s)     : none
321 *********************************************************************************************************
322 */
323 void TriggerSimulateInterrupt(rt_uint32_t IntIndex)
324 {
325     if((IntIndex < MAX_INTERRUPT_NUM) && (hInterruptEventMutex != NULL))
326     {
327         /* Yield interrupts are processed even when critical nesting is non-zero  */
328         WaitForSingleObject(hInterruptEventMutex,
329                             INFINITE);
330 
331         CpuPendingInterrupts |= (1 << IntIndex);
332 
333         SetEvent(hInterruptEventHandle);
334 
335         ReleaseMutex(hInterruptEventMutex);
336     }
337 } /*** TriggerSimulateInterrupt ***/
338 
339 /*
340 *********************************************************************************************************
341 *                                            RegisterSimulateInterrupt()
342 * Description : Register a interrupt handle to simulate paltform
343 * Argument(s) : rt_uint32_t IntIndex,rt_uint32_t (*IntHandler)(void)
344 * Return(s)   : void
345 * Caller(s)   : Applications
346 * Note(s)     : none
347 *********************************************************************************************************
348 */
349 void RegisterSimulateInterrupt(rt_uint32_t IntIndex,rt_uint32_t (*IntHandler)(void))
350 {
351     if(IntIndex < MAX_INTERRUPT_NUM)
352     {
353         if (hInterruptEventMutex != NULL)
354         {
355             WaitForSingleObject(hInterruptEventMutex,
356                                 INFINITE);
357 
358             CpuIsrHandler[IntIndex] = IntHandler;
359 
360             ReleaseMutex(hInterruptEventMutex);
361         }
362         else
363         {
364             CpuIsrHandler[IntIndex] = IntHandler;
365         }
366     }
367 
368 } /*** RegisterSimulateInterrupt ***/
369 
370 
371 
372 /*
373 *********************************************************************************************************
374 *                                             PRIVATE FUNCTION
375 *********************************************************************************************************
376 */
377 
378 /*
379 *********************************************************************************************************
380 *                                            WinThreadScheduler()
381 * Description : Handle all simulate interrupts
382 * Argument(s) : void
383 * Return(s)   : static void
384 * Caller(s)   : os scachle
385 * Note(s)     : none
386 *********************************************************************************************************
387 */
388 #define WIN_WM_MIN_RES      (1)
389  void WinThreadScheduler(void)
390 {
391     HANDLE          hInterruptObjectList[2];
392     HANDLE          hThreadHandle;
393     rt_uint32_t     SwitchRequiredMask;
394     rt_uint32_t     i;
395 
396     win_thread_t    *WinThreadFrom;
397     win_thread_t    *WinThreadTo;
398 
399     /*
400      * Install the interrupt handlers used bye scheduler itself
401      */
402     RegisterSimulateInterrupt(CPU_INTERRUPT_YIELD,
403                               YieldInterruptHandle);
404     RegisterSimulateInterrupt(CPU_INTERRUPT_TICK,
405                               SysTickInterruptHandle);
406 
407     /*
408      * Create the events and mutex that are used to synchronise all the WinThreads
409      */
410     hInterruptEventMutex = CreateMutex(NULL,
411                                        FALSE,
412                                        NULL);
413     hInterruptEventHandle = CreateEvent(NULL,
414                                         FALSE,
415                                         FALSE,
416                                         NULL);
417 
418     if((hInterruptEventMutex == NULL) || (hInterruptEventHandle == NULL))
419     {
420         return;
421     }
422 
423     /*
424      * Set the priority of this WinThread such that it is above the priority of the WinThreads
425      * that run rt-threads.
426      * This is higher priority is required to ensure simulate interrupts take priority over rt-threads
427      */
428     hThreadHandle = GetCurrentThread();
429     if(hThreadHandle == NULL)
430     {
431         return;
432     }
433 
434     if (SetThreadPriority(hThreadHandle,
435                           THREAD_PRIORITY_HIGHEST) == 0)
436     {
437         return;
438     }
439     SetThreadPriorityBoost(hThreadHandle,
440                            TRUE);
441     SetThreadAffinityMask(hThreadHandle,
442                           0x01);
443 
444     /*
445      * Start the thread that simulates the timer peripheral to generate tick interrupts.
446      */
447     OSTick_Thread = CreateThread(NULL,
448                                  0,
449                                  ThreadforSysTickTimer,
450                                  0,
451                                  CREATE_SUSPENDED,
452                                  &OSTick_ThreadID);
453     if(OSTick_Thread == NULL)
454     {
455         //Display Error Message
456 
457 
458         return;
459     }
460     SetThreadPriority(OSTick_Thread,
461                       THREAD_PRIORITY_NORMAL);
462     SetThreadPriorityBoost(OSTick_Thread,
463                            TRUE);
464     SetThreadAffinityMask(OSTick_Thread,
465                           0x01);
466 
467     /*
468      * Set timer Caps
469      */
470     if (timeGetDevCaps(&OSTick_TimerCap,
471                        sizeof(OSTick_TimerCap)) != TIMERR_NOERROR)
472     {
473 
474         CloseHandle(OSTick_Thread);
475 
476         return;
477     }
478     if (OSTick_TimerCap.wPeriodMin < WIN_WM_MIN_RES)
479     {
480         OSTick_TimerCap.wPeriodMin = WIN_WM_MIN_RES;
481     }
482 
483     if(timeBeginPeriod(OSTick_TimerCap.wPeriodMin) != TIMERR_NOERROR)
484     {
485         CloseHandle(OSTick_Thread);
486 
487         return;
488     }
489 
490     OSTick_SignalPtr = CreateEvent(NULL,TRUE,FALSE,NULL);
491     if(OSTick_SignalPtr == NULL)
492     {
493         // disp error message
494 
495         timeEndPeriod(OSTick_TimerCap.wPeriodMin);
496         CloseHandle(OSTick_Thread);
497 
498         return;
499     }
500 
501     OSTick_TimerID = timeSetEvent((UINT             )   (1000 / RT_TICK_PER_SECOND) ,
502                                   (UINT             )   OSTick_TimerCap.wPeriodMin,
503                                   (LPTIMECALLBACK   )   OSTick_SignalPtr,
504                                   (DWORD_PTR        )   NULL,
505                                   (UINT             )   (TIME_PERIODIC | TIME_CALLBACK_EVENT_SET));
506 
507     if(OSTick_TimerID == 0)
508     {
509         //disp
510 
511         CloseHandle(OSTick_SignalPtr);
512         timeEndPeriod(OSTick_TimerCap.wPeriodMin);
513         CloseHandle(OSTick_Thread);
514 
515         return;
516     }
517 
518     /*
519      * Start OS Tick Thread an release Interrupt Mutex
520      */
521     ResumeThread(OSTick_Thread);
522     ReleaseMutex( hInterruptEventMutex );
523 
524     //trigger YEILD INTERRUPT
525     TriggerSimulateInterrupt(CPU_INTERRUPT_YIELD);
526 
527     /*
528      * block on the mutex that ensure exclusive access to the simulated interrupt objects
529      *  and the events that signals that a simulated interrupt should be processed.
530      */
531 
532     hInterruptObjectList[0] = hInterruptEventHandle;
533     hInterruptObjectList[1] = hInterruptEventMutex;
534 
535 
536     while (1)
537     {
538         WaitForMultipleObjects(sizeof(hInterruptObjectList) / sizeof(HANDLE),
539                                hInterruptObjectList,
540                                TRUE,
541                                INFINITE);
542 
543         /*
544          * Used to indicate whether the simulate interrupt processing has necessitated a contex
545          * switch to another thread
546          */
547         SwitchRequiredMask = 0;
548 
549         /*
550          * For each interrupt we are interested in processing ,each of which is represented
551          * by a bit in the 32bit CpuPendingInterrupts variable.
552          */
553         for (i = 0; i < MAX_INTERRUPT_NUM; ++i)
554         {
555             /* is the simulated interrupt pending ? */
556             if (CpuPendingInterrupts & (1UL << i))
557             {
558                 /* Is a handler installed ?*/
559                 if (CpuIsrHandler[i] != NULL)
560                 {
561                     /* Run the actual handler */
562                     if (CpuIsrHandler[i]() != 0)
563                     {
564                         SwitchRequiredMask |= (1UL << i);
565                     }
566                 }
567 
568                 /* Clear the interrupt pending bit */
569                 CpuPendingInterrupts &= ~(1UL << i);
570             }
571         }
572 
573         if(SwitchRequiredMask != 0)
574         {
575             WinThreadFrom = (win_thread_t *)rt_interrupt_from_thread;
576             WinThreadTo = (win_thread_t *)rt_interrupt_to_thread;
577 
578             if ((WinThreadFrom != NULL) && (WinThreadFrom->ThreadHandle != NULL))
579             {
580                 SuspendThread(WinThreadFrom->ThreadHandle);
581             }
582 
583             ResumeThread(WinThreadTo->ThreadHandle);
584 
585         }
586 
587         ReleaseMutex(hInterruptEventMutex);
588     }
589 } /*** WinThreadScheduler ***/
590 
591 
592 
593 /*
594 *********************************************************************************************************
595 *                                            ThreadforSysTickTimer()
596 * Description : win thread to simulate a systick timer
597 * Argument(s) : LPVOID lpParam
598 * Return(s)   : static DWORD WINAPI
599 * Caller(s)   : none
600 * Note(s)     : This is not a real time way of generating tick events as the next wake time should be relative
601 *               to the previous wake time,not the time Sleep() is called.
602 *               It is done this way to prevent overruns in this very non real time simulated/emulated environment
603 *********************************************************************************************************
604 */
605 static DWORD WINAPI ThreadforSysTickTimer(LPVOID lpParam)
606 {
607 
608     (void)lpParam;              //prevent compiler warnings
609 
610     for(;;)
611     {
612         /*
613          * Wait until the timer expires and we can access the simulated interrupt variables.
614          */
615         WaitForSingleObject(OSTick_SignalPtr,INFINITE);
616 
617         ResetEvent(OSTick_SignalPtr);
618 
619         /*
620          * Trigger a systick interrupt
621          */
622         TriggerSimulateInterrupt(CPU_INTERRUPT_TICK);
623 
624     }
625 
626     return 0;
627 
628 } /*** prvThreadforSysTickTimer ***/
629 
630 /*
631 *********************************************************************************************************
632 *                                            SysTickInterruptHandle()
633 * Description : Interrupt handle for systick
634 * Argument(s) : void
635 * Return(s)   : rt_uint32_t
636 * Caller(s)   : none
637 * Note(s)     : none
638 *********************************************************************************************************
639 */
640 rt_uint32_t SysTickInterruptHandle(void)
641 {
642 
643     /* enter interrupt */
644     rt_interrupt_enter();
645 
646     rt_tick_increase();
647 
648     /* leave interrupt */
649     rt_interrupt_leave();
650 
651     return 0;
652 } /*** SysTickInterruptHandle ***/
653 
654 /*
655 *********************************************************************************************************
656 *                                            YieldInterruptHandle()
657 * Description : Interrupt handle for Yield
658 * Argument(s) : void
659 * Return(s)   : rt_uint32_t
660 * Caller(s)   : none
661 * Note(s)     : none
662 *********************************************************************************************************
663 */
664 rt_uint32_t YieldInterruptHandle(void)
665 {
666 
667     /*
668      * if rt_thread_switch_interrupt_flag = 1 yield already handled
669      */
670     if(rt_thread_switch_interrupt_flag != 0)
671     {
672         rt_thread_switch_interrupt_flag = 0;
673 
674         /* return thread switch request = 1 */
675         return 1;
676     }
677 
678     return 0;
679 } /*** YieldInterruptHandle ***/
680