ex10-threads-cooperative.c (4838B)
1 // Copyright 2022, Brian Swetland <swetland@frotz.net> 2 // Licensed under the Apache License, Version 2.0 3 4 #include <hw/debug.h> 5 #include <hw/riscv.h> 6 #include <hw/context.h> 7 #include <hw/intrinsics.h> 8 #include <hw/platform.h> 9 #include <hw/litex.h> 10 #include <gfx/gfx.h> 11 #include <string.h> 12 13 // (extremely simple) memory management 14 // ------------------------------------ 15 16 #define MEMORY_TOP (DRAM_BASE + DRAM_SIZE) 17 18 static uint8_t* next_stack = (void*) (MEMORY_TOP - 4096); 19 static uint8_t* next_heap = (void*) (DRAM_BASE + 1024*1024); 20 21 void* alloc_stack(void) { 22 void* s = next_stack; 23 next_stack -= 8192; 24 memset(next_stack, 0xee, 4096); 25 return s; 26 } 27 28 void* alloc(unsigned sz) { 29 sz = (sz + 31) & (~31); 30 uint8_t* ptr = next_heap; 31 memset(ptr, 0, sz); 32 next_heap += sz; 33 return ptr; 34 } 35 36 // (cooperative) thread library 37 // ---------------------------- 38 39 #define THREAD_MAGIC 0x5c95bec3 40 41 typedef struct thread { 42 uint32_t magic; 43 uint32_t id; 44 struct thread *next; 45 struct thread *prev; 46 cframe_t ctxt; 47 } thread_t; 48 49 static uint32_t next_thread_id = 1; 50 51 thread_t thread0 = { 52 .magic = THREAD_MAGIC, 53 .id = 0, 54 .next = &thread0, 55 .prev = &thread0, 56 }; 57 58 void thread_init(void) { 59 threadptr_set(&thread0); 60 }; 61 62 thread_t* thread_create(int (*fn)(void*), void* arg) { 63 thread_t *current = threadptr_get(); 64 65 thread_t *t = alloc(sizeof(thread_t)); 66 t->magic = THREAD_MAGIC; 67 t->id = next_thread_id++; 68 t->next = current; 69 t->prev = current->prev; 70 t->prev->next = t; 71 t->next->prev = t; 72 73 // setup threadptr and stackptr 74 t->ctxt.tp = (uintptr_t) t; 75 t->ctxt.sp = (uintptr_t) alloc_stack(); 76 77 // setup the context_entry thunk 78 t->ctxt.s0 = (uintptr_t) arg; 79 t->ctxt.s1 = 0; 80 t->ctxt.s2 = (uintptr_t) fn; 81 t->ctxt.pc = (uintptr_t) context_entry; 82 83 xprintf("[ created thread %p id=%u sp=%08x pc=%08x ]\n", 84 t, t->id, t->ctxt.sp, t->ctxt.s2); 85 86 return t; 87 } 88 89 void thread_exit(int status) { 90 thread_t *current = threadptr_get(); 91 thread_t *next = current->next; 92 thread_t *prev = current->prev; 93 94 xprintf("[ thread %p id=%u exited status=%d ]\n", current, current->id, status); 95 96 if (next == current) { 97 xprintf("[ all threads have exited ]\n"); 98 for (;;) ; 99 } 100 101 // remove current thread from list 102 next->prev = prev; 103 prev->next = next; 104 current->prev = 0; 105 current->next = 0; 106 107 // switch to next thread 108 context_switch(¤t->ctxt, &next->ctxt); 109 110 xprintf("[ error: zombie thread ]\n"); 111 for (;;) ; 112 } 113 114 void yield(void) { 115 thread_t* current = threadptr_get(); 116 context_switch(¤t->ctxt, ¤t->next->ctxt); 117 } 118 119 // multithreaded mandelbrot demo 120 // ----------------------------- 121 122 uint16_t colors[12] = { 123 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 124 0x7777, 0x8888, 0x9999, 0xAAAA, 0xBBBB, 0xCCCC, 125 }; 126 127 int render(gfx_surface_t* _gs, 128 uint32_t px0, uint32_t py0, 129 uint32_t px1, uint32_t py1) { 130 gfx_surface_t gs; 131 memcpy(&gs, _gs, sizeof(gs)); 132 133 for (int py = py0; py < py1; py++) { 134 int y0 = 650 - (1300 * py) / gs.height; 135 for (int px = px0; px < px1; px++) { 136 int x0 = -1250 + (1750 * px) / gs.width; 137 int x = 0, y = 0; 138 for (int i = 0; i < 500; i++) { 139 int x2 = x * x / 1000; 140 int y2 = y * y / 1000; 141 if ((x2 + y2) > 4000) { 142 gs.fgcolor = colors[(i > 11) ? 11 : i]; 143 gfx_plot(&gs, px, py); 144 goto done; 145 } 146 y = 2 * x * y / 1000 + y0; 147 x = x2 - y2 + x0; 148 } 149 gs.fgcolor = 0; 150 gfx_plot(&gs, px, py); 151 done: 152 ; 153 } 154 yield(); 155 } 156 157 return 2; 158 } 159 160 int t1(void* arg) { 161 gfx_surface_t *gs = arg; 162 gfx_fill(gs, 0, 0, 320, 240, gfx_color(gs, C_WHITE)); 163 gfx_puts(gs, 0, 240-16, "Thread One"); 164 return render(gs, 0, 0, 320, 240); 165 } 166 int t2(void* arg) { 167 gfx_surface_t *gs = arg; 168 gfx_fill(gs, 320, 240, 640, 480, gfx_color(gs, C_GRAY75)); 169 gfx_puts(gs, 320, 480-16, "Thread Two"); 170 return render(gs, 320, 240, 640, 480); 171 } 172 int t3(void* arg) { 173 gfx_surface_t *gs = arg; 174 gfx_fill(gs, 320, 0, 640, 240, gfx_color(gs, C_GRAY25)); 175 gfx_puts(gs, 320, 240-16, "Thread Three"); 176 return render(gs, 320, 0, 640, 240); 177 } 178 int t4(void* arg) { 179 gfx_surface_t *gs = arg; 180 gfx_fill(gs, 0, 240, 320, 480, gfx_color(gs, C_GRAY50)); 181 gfx_puts(gs, 0, 480-16, "Thread Four"); 182 return render(gs, 0, 240, 320, 480); 183 } 184 185 // program start 186 // ------------- 187 188 void interrupt_handler(void) { } 189 190 void exception_handler(eframe_t *ef) { 191 xprintf("\n** SUPERVISOR EXCEPTION **\n"); 192 xprint_s_exception(ef); 193 xprintf("\nHALT\n"); 194 for (;;) ; 195 } 196 197 void start(void) { 198 xprintf("Example 10 - Threads Cooperative\n\n"); 199 200 csr_write(CSR_STVEC, (uintptr_t) trap_entry); 201 202 // setup the main thread (running now) 203 thread_init(); 204 205 // setup graphics 206 gfx_surface_t *gs = alloc(sizeof(*gs)); 207 gfx_init_display(gs); 208 memset((void*) gs->pixels, 0, gs->width * gs->height * 2); 209 210 // launch four worker threads 211 thread_create(t1, gs); 212 thread_create(t2, gs); 213 thread_create(t3, gs); 214 thread_create(t4, gs); 215 216 // exit the main thread 217 thread_exit(0); 218 } 219