1 | /*
|
2 | Copyright 2014 Marc Prager
|
3 |
|
4 | Multithreading example.
|
5 | */
|
6 |
|
7 | #include <armv6-m/tl.h> // context switcher for Cortex-M0
|
8 |
|
9 | #include <lpc8/syscon.h>
|
10 | #include <lpc8/swm.h>
|
11 | #include <lpc8/usart.h>
|
12 | #include <lpc8/systickTimer.h>
|
13 |
|
14 | #include <print.h>
|
15 | #include <fixedPoint.h>
|
16 |
|
17 | enum {
|
18 | PCLK = 12*MEGA,
|
19 | BAUD = 115200,
|
20 | };
|
21 |
|
22 | void handlerDefault(void) {
|
23 | printString("CRASHED!! - STOP.");
|
24 | while(true);
|
25 | }
|
26 |
|
27 | void sleepMs(TlContext *ctx, int ms) {
|
28 | int timeoutMs = systickTimerMs + ms;
|
29 | while (timeoutMs-systickTimerMs > 0) tlYield(ctx);
|
30 | }
|
31 |
|
32 | __attribute__((naked)) Uint32 getSp(void) {
|
33 | asm("mov r0,sp"); // AAPCS: r0 = function return
|
34 | asm("bx lr");
|
35 | }
|
36 |
|
37 | #define TOP_OF_STACK(s) (&s[sizeof s/sizeof s[0]])
|
38 |
|
39 | // prototypes: signature enforcement
|
40 | TlLoop
|
41 | threadA, // printing 'A' every 500ms
|
42 | threadB, // printing 'B<num>' every 2s, num counting up
|
43 | threadT, // complex thread, calling 2 child threads every second
|
44 | threadT1, // printing 'T1', then yield
|
45 | threadT2; // printing 'T2', then yield
|
46 |
|
47 | void threadA(TlContext *ctx) {
|
48 | tlYield(ctx);
|
49 | while(true) {
|
50 | printChar('A');
|
51 | sleepMs(ctx,500);
|
52 | }
|
53 | }
|
54 |
|
55 | void threadB(TlContext *ctx) {
|
56 | tlYield(ctx);
|
57 | for (int i=0; ; i++) {
|
58 | printChar('B'); printUDec32(i); printChar(' ');
|
59 | sleepMs(ctx,2000);
|
60 | }
|
61 | }
|
62 |
|
63 |
|
64 | void threadT(TlContext *ctx) {
|
65 | Uint32 stackT1[64], stackT2[64]; // complex task, needs more threads
|
66 | // these stacks reside on the local stack of 'T'
|
67 |
|
68 | TlContext
|
69 | ctxT1 = { TOP_OF_STACK(stackT1) },
|
70 | ctxT2 = { TOP_OF_STACK(stackT2) };
|
71 |
|
72 | tlCreate(&ctxT1,threadT1);
|
73 | tlCreate(&ctxT2,threadT2);
|
74 |
|
75 |
|
76 | printString("SP in T="); printHex32(getSp()); printLn();
|
77 |
|
78 | tlYield(ctx);
|
79 | while (true) {
|
80 | tlCall(&ctxT1);
|
81 | tlCall(&ctxT2);
|
82 | sleepMs(ctx,1000);
|
83 | }
|
84 | }
|
85 |
|
86 | void threadT1(TlContext *ctx) {
|
87 | while (true) {
|
88 | tlYield(ctx);
|
89 | printString("T1");
|
90 | }
|
91 | }
|
92 |
|
93 | void threadT2(TlContext *ctx) {
|
94 | while (true) {
|
95 | tlYield(ctx);
|
96 | printString("T2");
|
97 | }
|
98 | }
|
99 |
|
100 | void runThreads(void);
|
101 |
|
102 | void main(void) {
|
103 | systickTimerInit(PCLK / 100/*Hz*/, 10/*ms*/ );
|
104 |
|
105 | SYSCON
|
106 | .ahbClkCtrl |= 1<<_SYSCON_AHBCLKCTRL_SWM
|
107 | | 1<<_SYSCON_AHBCLKCTRL_USART0
|
108 | ;
|
109 |
|
110 | swmPin(SWM_FUNCTION_U0_RXD,0);
|
111 | swmPin(SWM_FUNCTION_U0_TXD,4);
|
112 |
|
113 | usart0InitFrom12Mhz (1);
|
114 | print (usart0BlockingWriteChar);
|
115 |
|
116 | printString("Running.\n");
|
117 | printString("SP before="); printHex32(getSp()); printLn();
|
118 |
|
119 | runThreads();
|
120 |
|
121 | printString("\nSP after="); printHex32(getSp()); printLn();
|
122 |
|
123 | // all threads and their stacks gone by at this point...
|
124 | printString("Terminated.\n");
|
125 | }
|
126 |
|
127 | void runThreads(void) {
|
128 | Uint32 stackA[64];
|
129 | Uint32 stackB[64];
|
130 | Uint32 stackT[3*64];
|
131 |
|
132 | TlContext
|
133 | ctxA = { TOP_OF_STACK(stackA) },
|
134 | ctxB = { TOP_OF_STACK(stackB) },
|
135 | ctxT = { TOP_OF_STACK(stackT) };
|
136 |
|
137 | tlCreate(&ctxA,threadA);
|
138 | tlCreate(&ctxB,threadB);
|
139 | tlCreate(&ctxT,threadT);
|
140 |
|
141 | printString("SP in scheduler="); printHex32(getSp()); printLn();
|
142 |
|
143 | // Round robin scheduler, 10 seconds lifetime.
|
144 | for (int tMs=systickTimerMs+10*1000; tMs-systickTimerMs>=0; ) {
|
145 | tlCall(&ctxA);
|
146 | tlCall(&ctxB);
|
147 | tlCall(&ctxT);
|
148 | }
|
149 | }
|