PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

680 lines
19KB

  1. /*
  2. * Threads.cpp - Library for threading on the Teensy.
  3. *
  4. *******************
  5. *
  6. * Copyright 2017 by Fernando Trias.
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
  9. * and associated documentation files (the "Software"), to deal in the Software without restriction,
  10. * including without limitation the rights to use, copy, modify, merge, publish, distribute,
  11. * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in all copies or
  15. * substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
  18. * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  20. * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. *
  23. *******************
  24. */
  25. #include "TeensyThreads.h"
  26. #include <Arduino.h>
  27. #ifndef __IMXRT1062__
  28. #include <IntervalTimer.h>
  29. IntervalTimer context_timer;
  30. #endif
  31. Threads threads;
  32. unsigned int time_start;
  33. unsigned int time_end;
  34. #define __flush_cpu() __asm__ volatile("DMB");
  35. // These variables are used by the assembly context_switch() function.
  36. // They are copies or pointers to data in Threads and ThreadInfo
  37. // and put here seperately in order to simplify the code.
  38. extern "C" {
  39. int currentUseSystick; // using Systick vs PIT/GPT
  40. int currentActive; // state of the system (first, start, stop)
  41. int currentCount;
  42. ThreadInfo *currentThread; // the thread currently running
  43. void *currentSave;
  44. int currentMSP; // Stack pointers to save
  45. void *currentSP;
  46. void loadNextThread() {
  47. threads.getNextThread();
  48. }
  49. }
  50. extern "C" void stack_overflow_default_isr() {
  51. currentThread->flags = Threads::ENDED;
  52. }
  53. extern "C" void stack_overflow_isr(void) __attribute__ ((weak, alias("stack_overflow_default_isr")));
  54. extern unsigned long _estack; // the main thread 0 stack
  55. // static void threads_svcall_isr(void);
  56. // static void threads_systick_isr(void);
  57. IsrFunction Threads::save_systick_isr;
  58. IsrFunction Threads::save_svcall_isr;
  59. /*
  60. * Teensy 3:
  61. * Replace the SysTick interrupt for our context switching. Note that
  62. * this function is "naked" meaning it does not save it's registers
  63. * on the stack. This is so we can preserve the stack of the caller.
  64. *
  65. * Interrupts will save r0-r4 in the stack and since this function
  66. * is short and simple, it should only use those registers. In the
  67. * future, this should be coded in assembly to make sure.
  68. */
  69. extern volatile uint32_t systick_millis_count;
  70. extern "C" void systick_isr();
  71. void __attribute((naked, noinline)) threads_systick_isr(void)
  72. {
  73. if (Threads::save_systick_isr) {
  74. asm volatile("push {r0-r4,lr}");
  75. (*Threads::save_systick_isr)();
  76. asm volatile("pop {r0-r4,lr}");
  77. }
  78. // TODO: Teensyduino 1.38 calls MillisTimer::runFromTimer() from SysTick
  79. if (currentUseSystick) {
  80. // we branch in order to preserve LR and the stack
  81. __asm volatile("b context_switch");
  82. }
  83. __asm volatile("bx lr");
  84. }
  85. void __attribute((naked, noinline)) threads_svcall_isr(void)
  86. {
  87. if (Threads::save_svcall_isr) {
  88. asm volatile("push {r0-r4,lr}");
  89. (*Threads::save_svcall_isr)();
  90. asm volatile("pop {r0-r4,lr}");
  91. }
  92. // Get the right stack so we can extract the PC (next instruction)
  93. // and then see the SVC calling instruction number
  94. __asm volatile("TST lr, #4 \n"
  95. "ITE EQ \n"
  96. "MRSEQ r0, msp \n"
  97. "MRSNE r0, psp \n");
  98. register unsigned int *rsp __asm("r0");
  99. unsigned int svc = ((uint8_t*)rsp[6])[-2];
  100. if (svc == Threads::SVC_NUMBER) {
  101. __asm volatile("b context_switch_direct");
  102. }
  103. else if (svc == Threads::SVC_NUMBER_ACTIVE) {
  104. currentActive = Threads::STARTED;
  105. __asm volatile("b context_switch_direct_active");
  106. }
  107. __asm volatile("bx lr");
  108. }
  109. #ifdef __IMXRT1062__
  110. /*
  111. *
  112. * Teensy 4:
  113. * Use unused GPT timers for context switching
  114. */
  115. extern "C" void unused_interrupt_vector(void);
  116. static void __attribute((naked, noinline)) gpt1_isr() {
  117. GPT1_SR |= GPT_SR_OF1; // clear set bit
  118. __asm volatile ("dsb"); // see github bug #20 by manitou48
  119. __asm volatile("b context_switch");
  120. }
  121. static void __attribute((naked, noinline)) gpt2_isr() {
  122. GPT2_SR |= GPT_SR_OF1; // clear set bit
  123. __asm volatile ("dsb"); // see github bug #20 by manitou48
  124. __asm volatile("b context_switch");
  125. }
  126. bool gtp1_init(unsigned int microseconds)
  127. {
  128. // Initialization code derived from @manitou48.
  129. // See https://github.com/manitou48/teensy4/blob/master/gpt_isr.ino
  130. // See https://forum.pjrc.com/threads/54265-Teensy-4-testing-mbed-NXP-MXRT1050-EVKB-(600-Mhz-M7)?p=193217&viewfull=1#post193217
  131. // keep track of which GPT timer we are using
  132. static int gpt_number = 0;
  133. // not configured yet, so find an inactive GPT timer
  134. if (gpt_number == 0) {
  135. if (! NVIC_IS_ENABLED(IRQ_GPT1)) {
  136. attachInterruptVector(IRQ_GPT1, &gpt1_isr);
  137. NVIC_SET_PRIORITY(IRQ_GPT1, 255);
  138. NVIC_ENABLE_IRQ(IRQ_GPT1);
  139. gpt_number = 1;
  140. }
  141. else if (! NVIC_IS_ENABLED(IRQ_GPT2)) {
  142. attachInterruptVector(IRQ_GPT2, &gpt2_isr);
  143. NVIC_SET_PRIORITY(IRQ_GPT2, 255);
  144. NVIC_ENABLE_IRQ(IRQ_GPT2);
  145. gpt_number = 2;
  146. }
  147. else {
  148. // if neither timer is free, we fail
  149. return false;
  150. }
  151. }
  152. switch (gpt_number) {
  153. case 1:
  154. CCM_CCGR1 |= CCM_CCGR1_GPT(CCM_CCGR_ON) ; // enable GPT1 module
  155. GPT1_CR = 0; // disable timer
  156. GPT1_PR = 23; // prescale: divide by 24 so 1 tick = 1 microsecond at 24MHz
  157. GPT1_OCR1 = microseconds - 1; // compare value
  158. GPT1_SR = 0x3F; // clear all prior status
  159. GPT1_IR = GPT_IR_OF1IE; // use first timer
  160. GPT1_CR = GPT_CR_EN | GPT_CR_CLKSRC(1) ; // set to peripheral clock (24MHz)
  161. break;
  162. case 2:
  163. CCM_CCGR1 |= CCM_CCGR1_GPT(CCM_CCGR_ON) ; // enable GPT1 module
  164. GPT2_CR = 0; // disable timer
  165. GPT2_PR = 23; // prescale: divide by 24 so 1 tick = 1 microsecond at 24MHz
  166. GPT2_OCR1 = microseconds - 1; // compare value
  167. GPT2_SR = 0x3F; // clear all prior status
  168. GPT2_IR = GPT_IR_OF1IE; // use first timer
  169. GPT2_CR = GPT_CR_EN | GPT_CR_CLKSRC(1) ; // set to peripheral clock (24MHz)
  170. break;
  171. default:
  172. return false;
  173. }
  174. return true;
  175. }
  176. #endif
  177. Threads::Threads() : current_thread(0), thread_count(0), thread_error(0) {
  178. // initilize thread slots to empty
  179. for(int i=0; i<MAX_THREADS; i++) {
  180. threadp[i] = NULL;
  181. }
  182. // fill thread 0, which is always running
  183. threadp[0] = new ThreadInfo();
  184. // initialize context_switch() globals from thread 0, which is MSP and always running
  185. currentThread = threadp[0]; // thread 0 is active
  186. currentSave = &threadp[0]->save;
  187. currentMSP = 1;
  188. currentSP = 0;
  189. currentCount = Threads::DEFAULT_TICKS;
  190. currentActive = FIRST_RUN;
  191. threadp[0]->flags = RUNNING;
  192. threadp[0]->ticks = DEFAULT_TICKS;
  193. threadp[0]->stack = (uint8_t*)&_estack - DEFAULT_STACK0_SIZE;
  194. threadp[0]->stack_size = DEFAULT_STACK0_SIZE;
  195. #ifdef __IMXRT1062__
  196. // commandeer SVCall & use GTP1 Interrupt
  197. save_svcall_isr = _VectorsRam[11];
  198. if (save_svcall_isr == unused_interrupt_vector) save_svcall_isr = 0;
  199. _VectorsRam[11] = threads_svcall_isr;
  200. currentUseSystick = 0; // disable Systick calls
  201. gtp1_init(1000); // tick every millisecond
  202. #else
  203. currentUseSystick = 1;
  204. // commandeer the SVCall & SysTick Exceptions
  205. save_svcall_isr = _VectorsRam[11];
  206. if (save_svcall_isr == unused_isr) save_svcall_isr = 0;
  207. _VectorsRam[11] = threads_svcall_isr;
  208. save_systick_isr = _VectorsRam[15];
  209. if (save_systick_isr == unused_isr) save_systick_isr = 0;
  210. _VectorsRam[15] = threads_systick_isr;
  211. #ifdef DEBUG
  212. #if defined(__MK20DX256__) || defined(__MK20DX128__)
  213. ARM_DEMCR |= ARM_DEMCR_TRCENA; // Make ssure Cycle Counter active
  214. ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
  215. #endif
  216. #endif
  217. #endif
  218. }
  219. /*
  220. * start() - Begin threading
  221. */
  222. int Threads::start(int prev_state) {
  223. __disable_irq();
  224. int old_state = currentActive;
  225. if (prev_state == -1) prev_state = STARTED;
  226. currentActive = prev_state;
  227. __enable_irq();
  228. return old_state;
  229. }
  230. /*
  231. * stop() - Stop threading, even if active.
  232. *
  233. * If threads have already started, this should be called sparingly
  234. * because it could destabalize the system if thread 0 is stopped.
  235. */
  236. int Threads::stop() {
  237. __disable_irq();
  238. int old_state = currentActive;
  239. currentActive = STOPPED;
  240. __enable_irq();
  241. return old_state;
  242. }
  243. /*
  244. * getNextThread() - Find next running thread
  245. *
  246. * This will also set the context_switcher() state variables
  247. */
  248. void Threads::getNextThread() {
  249. #ifdef DEBUG
  250. // Keep track of the number of cycles expended by each thread.
  251. // See @dfragster: https://forum.pjrc.com/threads/41504-Teensy-3-x-multithreading-library-first-release?p=213086#post213086
  252. currentThread->cyclesAccum += ARM_DWT_CYCCNT - currentThread->cyclesStart;
  253. #endif
  254. // First, save the currentSP set by context_switch
  255. currentThread->sp = currentSP;
  256. // did we overflow the stack (don't check thread 0)?
  257. // allow an extra 8 bytes for a call to the ISR and one additional call or variable
  258. if (current_thread && ((uint8_t*)currentThread->sp - currentThread->stack <= 8)) {
  259. stack_overflow_isr();
  260. }
  261. // Find the next running thread
  262. while(1) {
  263. current_thread++;
  264. if (current_thread >= MAX_THREADS) {
  265. current_thread = 0; // thread 0 is MSP; always active so return
  266. break;
  267. }
  268. if (threadp[current_thread] && threadp[current_thread]->flags == RUNNING) break;
  269. }
  270. currentCount = threadp[current_thread]->ticks;
  271. currentThread = threadp[current_thread];
  272. currentSave = &threadp[current_thread]->save;
  273. currentMSP = (current_thread==0?1:0);
  274. currentSP = threadp[current_thread]->sp;
  275. #ifdef DEBUG
  276. currentThread->cyclesStart = ARM_DWT_CYCCNT;
  277. #endif
  278. }
  279. /*
  280. * Empty placeholder for IntervalTimer class
  281. */
  282. static void context_pit_empty() {}
  283. /*
  284. * Store the PIT timer flag register for use in assembly
  285. */
  286. volatile uint32_t *context_timer_flag;
  287. /*
  288. * Defined in assembly code
  289. */
  290. extern "C" void context_switch_pit_isr();
  291. /*
  292. * Stop using the SysTick interrupt and start using
  293. * the IntervalTimer timer. The parameter is the number of microseconds
  294. * for each tick.
  295. */
  296. int Threads::setMicroTimer(int tick_microseconds)
  297. {
  298. #ifdef __IMXRT1062__
  299. gtp1_init(tick_microseconds);
  300. #else
  301. /*
  302. * Implementation strategy suggested by @tni in Teensy Forums; see
  303. * https://forum.pjrc.com/threads/41504-Teensy-3-x-multithreading-library-first-release
  304. */
  305. // lowest priority so we don't interrupt other interrupts
  306. context_timer.priority(255);
  307. // start timer with dummy fuction
  308. if (context_timer.begin(context_pit_empty, tick_microseconds) == 0) {
  309. // failed to set the timer!
  310. return 0;
  311. }
  312. currentUseSystick = 0; // disable Systick calls
  313. // get the PIT number [0-3] (IntervalTimer overrides IRQ_NUMBER_t op)
  314. int number = (IRQ_NUMBER_t)context_timer - IRQ_PIT_CH0;
  315. // calculate number of uint32_t per PIT; should be 4.
  316. // Not hard-coded in case this changes in future CPUs.
  317. const int width = (PIT_TFLG1 - PIT_TFLG0) / sizeof(uint32_t);
  318. // get the right flag to ackowledge PIT interrupt
  319. context_timer_flag = &PIT_TFLG0 + (width * number);
  320. attachInterruptVector(context_timer, context_switch_pit_isr);
  321. #endif
  322. return 1;
  323. }
  324. /*
  325. * Set each time slice to be 'microseconds' long
  326. */
  327. int Threads::setSliceMicros(int microseconds)
  328. {
  329. setMicroTimer(microseconds);
  330. setDefaultTimeSlice(1);
  331. return 1;
  332. }
  333. /*
  334. * Set each time slice to be 'milliseconds' long
  335. */
  336. int Threads::setSliceMillis(int milliseconds)
  337. {
  338. if (currentUseSystick) {
  339. setDefaultTimeSlice(milliseconds);
  340. }
  341. else {
  342. // if we're using the PIT, we should probably really disable it and
  343. // re-establish the systick timer; but this is easier for now
  344. setSliceMicros(milliseconds * 1000);
  345. }
  346. return 1;
  347. }
  348. /*
  349. * del_process() - This is called when the task returns
  350. *
  351. * Turns thread off. Thread continues running until next call to
  352. * context_switch() at which point it all stops. The while(1) statement
  353. * just stalls until such time.
  354. */
  355. void Threads::del_process(void)
  356. {
  357. int old_state = threads.stop();
  358. ThreadInfo *me = threads.threadp[threads.current_thread];
  359. // Would love to delete stack here but the thread doesn't
  360. // end now. It continues until the next tick.
  361. // if (me->my_stack) {
  362. // delete[] me->stack;
  363. // me->stack = 0;
  364. // }
  365. threads.thread_count--;
  366. me->flags = ENDED; //clear the flags so thread can stop and be reused
  367. threads.start(old_state);
  368. while(1); // just in case, keep working until context change when execution will not return to this thread
  369. }
  370. /*
  371. * Initializes a thread's stack. Called when thread is created
  372. */
  373. void *Threads::loadstack(ThreadFunction p, void * arg, void *stackaddr, int stack_size)
  374. {
  375. interrupt_stack_t * process_frame = (interrupt_stack_t *)((uint8_t*)stackaddr + stack_size - sizeof(interrupt_stack_t) - 8);
  376. process_frame->r0 = (uint32_t)arg;
  377. process_frame->r1 = 0;
  378. process_frame->r2 = 0;
  379. process_frame->r3 = 0;
  380. process_frame->r12 = 0;
  381. process_frame->lr = (uint32_t)Threads::del_process;
  382. process_frame->pc = ((uint32_t)p);
  383. process_frame->xpsr = 0x1000000;
  384. uint8_t *ret = (uint8_t*)process_frame;
  385. // ret -= sizeof(software_stack_t); // uncomment this if we are saving R4-R11 to the stack
  386. return (void*)ret;
  387. }
  388. /*
  389. * Add a new thread to the queue.
  390. * add_thread(fund, arg)
  391. *
  392. * fund : is a function pointer. The function prototype is:
  393. * void *func(void *param)
  394. * arg : is a void pointer that is passed as the first parameter
  395. * of the function. In the example above, arg is passed
  396. * as param.
  397. * stack_size : the size of the buffer pointed to by stack. If
  398. * it is 0, then "stack" must also be 0. If so, the function
  399. * will allocate the default stack size of the heap using new().
  400. * stack : pointer to new data stack of size stack_size. If this is 0,
  401. * then it will allocate a stack on the heap using new() of size
  402. * stack_size. If stack_size is 0, a default size will be used.
  403. * return: an integer ID to be used for other calls
  404. */
  405. int Threads::addThread(ThreadFunction p, void * arg, int stack_size, void *stack)
  406. {
  407. int old_state = stop();
  408. if (stack_size == -1) stack_size = DEFAULT_STACK_SIZE;
  409. for (int i=1; i < MAX_THREADS; i++) {
  410. if (threadp[i] == NULL) { // empty thread, so fill it
  411. threadp[i] = new ThreadInfo();
  412. }
  413. if (threadp[i]->flags == ENDED || threadp[i]->flags == EMPTY) { // free thread
  414. ThreadInfo *tp = threadp[i]; // working on this thread
  415. if (tp->stack && tp->my_stack) {
  416. delete[] tp->stack;
  417. }
  418. if (stack==0) {
  419. stack = new uint8_t[stack_size];
  420. tp->my_stack = 1;
  421. }
  422. else {
  423. tp->my_stack = 0;
  424. }
  425. tp->stack = (uint8_t*)stack;
  426. tp->stack_size = stack_size;
  427. void *psp = loadstack(p, arg, tp->stack, tp->stack_size);
  428. tp->sp = psp;
  429. tp->ticks = DEFAULT_TICKS;
  430. tp->flags = RUNNING;
  431. tp->save.lr = 0xFFFFFFF9;
  432. #ifdef DEBUG
  433. tp->cyclesStart = ARM_DWT_CYCCNT;
  434. tp->cyclesAccum = 0;
  435. #endif
  436. currentActive = old_state;
  437. thread_count++;
  438. if (old_state == STARTED || old_state == FIRST_RUN) start();
  439. return i;
  440. }
  441. }
  442. if (old_state == STARTED) start();
  443. return -1;
  444. }
  445. int Threads::getState(int id)
  446. {
  447. return threadp[id]->flags;
  448. }
  449. int Threads::setState(int id, int state)
  450. {
  451. threadp[id]->flags = state;
  452. return state;
  453. }
  454. int Threads::wait(int id, unsigned int timeout_ms)
  455. {
  456. unsigned int start = millis();
  457. // need to store state in temp volatile memory for optimizer.
  458. // "while (thread[id].flags != RUNNING)" will be optimized away
  459. volatile int state;
  460. while (1) {
  461. if (timeout_ms != 0 && millis() - start > timeout_ms) return -1;
  462. state = threadp[id]->flags;
  463. if (state != RUNNING) break;
  464. yield();
  465. }
  466. return id;
  467. }
  468. int Threads::kill(int id)
  469. {
  470. threadp[id]->flags = ENDED;
  471. return id;
  472. }
  473. int Threads::suspend(int id)
  474. {
  475. threadp[id]->flags = SUSPENDED;
  476. return id;
  477. }
  478. int Threads::restart(int id)
  479. {
  480. threadp[id]->flags = RUNNING;
  481. return id;
  482. }
  483. void Threads::setTimeSlice(int id, unsigned int ticks)
  484. {
  485. threadp[id]->ticks = ticks - 1;
  486. }
  487. void Threads::setDefaultTimeSlice(unsigned int ticks)
  488. {
  489. DEFAULT_TICKS = ticks - 1;
  490. }
  491. void Threads::setDefaultStackSize(unsigned int bytes_size)
  492. {
  493. DEFAULT_STACK_SIZE = bytes_size;
  494. }
  495. void Threads::yield() {
  496. __asm volatile("svc %0" : : "i"(Threads::SVC_NUMBER));
  497. }
  498. void Threads::yield_and_start() {
  499. __asm volatile("svc %0" : : "i"(Threads::SVC_NUMBER_ACTIVE));
  500. }
  501. void Threads::delay(int millisecond) {
  502. int mx = millis();
  503. while((int)millis() - mx < millisecond) yield();
  504. }
  505. int Threads::id() {
  506. volatile int ret;
  507. __disable_irq();
  508. ret = current_thread;
  509. __enable_irq();
  510. return ret;
  511. }
  512. int Threads::getStackUsed(int id) {
  513. return threadp[id]->stack + threadp[id]->stack_size - (uint8_t*)threadp[id]->sp;
  514. }
  515. int Threads::getStackRemaining(int id) {
  516. return (uint8_t*)threadp[id]->sp - threadp[id]->stack;
  517. }
  518. #ifdef DEBUG
  519. unsigned long Threads::getCyclesUsed(int id) {
  520. stop();
  521. unsigned long ret = threadp[id]->cyclesAccum;
  522. start();
  523. return ret;
  524. }
  525. #endif
  526. /*
  527. * On creation, stop threading and save state
  528. */
  529. Threads::Suspend::Suspend() {
  530. __disable_irq();
  531. save_state = currentActive;
  532. currentActive = 0;
  533. __enable_irq();
  534. }
  535. /*
  536. * On destruction, restore threading state
  537. */
  538. Threads::Suspend::~Suspend() {
  539. __disable_irq();
  540. currentActive = save_state;
  541. __enable_irq();
  542. }
  543. int Threads::Mutex::getState() {
  544. int p = threads.stop();
  545. int ret = state;
  546. threads.start(p);
  547. return ret;
  548. }
  549. int __attribute__ ((noinline)) Threads::Mutex::lock(unsigned int timeout_ms) {
  550. if (try_lock()) return 1; // we're good, so avoid more checks
  551. uint32_t start = systick_millis_count;
  552. while (1) {
  553. if (try_lock()) return 1;
  554. if (timeout_ms && (systick_millis_count - start > timeout_ms)) return 0;
  555. if (waitthread==-1) { // can hold 1 thread suspend until unlock
  556. int p = threads.stop();
  557. waitthread = threads.current_thread;
  558. waitcount = currentCount;
  559. threads.suspend(waitthread);
  560. threads.start(p);
  561. }
  562. threads.yield();
  563. }
  564. __flush_cpu();
  565. return 0;
  566. }
  567. int Threads::Mutex::try_lock() {
  568. int p = threads.stop();
  569. if (state == 0) {
  570. state = 1;
  571. threads.start(p);
  572. return 1;
  573. }
  574. threads.start(p);
  575. return 0;
  576. }
  577. int __attribute__ ((noinline)) Threads::Mutex::unlock() {
  578. int p = threads.stop();
  579. if (state==1) {
  580. state = 0;
  581. if (waitthread >= 0) { // reanimate a suspended thread waiting for unlock
  582. threads.restart(waitthread);
  583. waitthread = -1;
  584. __flush_cpu();
  585. threads.yield_and_start();
  586. return 1;
  587. }
  588. }
  589. __flush_cpu();
  590. threads.start(p);
  591. return 1;
  592. }