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