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