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