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
SetThreadName(DWORD dwThreadID,char * threadName)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
thread_run(LPVOID lpThreadParameter)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
rt_hw_stack_init(void * pEntry,void * pParam,rt_uint8_t * pStackAddr,void * pExit)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 */
rt_hw_interrupt_disable(void)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 */
rt_hw_interrupt_enable(rt_base_t level)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 */
rt_hw_context_switch_interrupt(rt_uint32_t from,rt_uint32_t to)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
rt_hw_context_switch(rt_uint32_t from,rt_uint32_t to)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 */
rt_hw_context_switch_to(rt_uint32_t to)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 */
TriggerSimulateInterrupt(rt_uint32_t IntIndex)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 */
RegisterSimulateInterrupt(rt_uint32_t IntIndex,rt_uint32_t (* IntHandler)(void))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)
WinThreadScheduler(void)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 */
ThreadforSysTickTimer(LPVOID lpParam)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 */
SysTickInterruptHandle(void)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 */
YieldInterruptHandle(void)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