ex11-threads-preemptive.c (6021B)
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 -= 4096; 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 void thread_exit(int status); 63 64 thread_t* thread_create(int (*fn)(void*), void* arg) { 65 irq_disable(); 66 67 thread_t *current = threadptr_get(); 68 69 thread_t *t = alloc(sizeof(thread_t)); 70 t->magic = THREAD_MAGIC; 71 t->id = next_thread_id++; 72 t->next = current; 73 t->prev = current->prev; 74 t->prev->next = t; 75 t->next->prev = t; 76 77 // setup threadptr and stackptr 78 t->ctxt.tp = (uintptr_t) t; 79 t->ctxt.sp = (uintptr_t) alloc_stack(); 80 t->ctxt.pc = (uintptr_t) trap_exit; 81 82 // setup a trap frame to allow us to "return" 83 // into the new thread the first time we switch to it 84 t->ctxt.sp -= (32 * 4); 85 tframe_t *frame = (void*) t->ctxt.sp; 86 memset(frame, 0, sizeof(*frame)); 87 frame->ra = (uintptr_t) thread_exit; 88 frame->a0 = (uintptr_t) arg; 89 frame->pc = (uintptr_t) fn; 90 91 xprintf("[ created thread %p id=%u sp=%08x pc=%08x ]\n", 92 t, t->id, t->ctxt.sp, frame->pc); 93 94 irq_enable(); 95 return t; 96 } 97 98 void thread_exit(int status) { 99 // disable interrupts to protect thread list manipulation 100 irq_disable(); 101 102 // make sure privilege and interrupt status are correct 103 // for when sret is executed after the context switch 104 csr_set(CSR_SSTATUS, SSTATUS_SPP | SSTATUS_SPIE); 105 106 thread_t *current = threadptr_get(); 107 thread_t *next = current->next; 108 thread_t *prev = current->prev; 109 110 xprintf("[ thread %p id=%u exited status=%d ]\n", current, current->id, status); 111 112 if (next == current) { 113 xprintf("[ all threads have exited ]\n"); 114 for (;;) ; 115 } 116 117 // remove current thread from list 118 next->prev = prev; 119 prev->next = next; 120 current->prev = 0; 121 current->next = 0; 122 123 // switch to next thread 124 context_switch(¤t->ctxt, &next->ctxt); 125 126 xprintf("[ error: zombie thread ]\n"); 127 for (;;) ; 128 } 129 130 // multithreaded mandelbrot demo 131 // ----------------------------- 132 133 uint16_t colors[12] = { 134 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 135 0x7777, 0x8888, 0x9999, 0xAAAA, 0xBBBB, 0xCCCC, 136 }; 137 138 int render(gfx_surface_t* _gs, 139 uint32_t px0, uint32_t py0, 140 uint32_t px1, uint32_t py1) { 141 gfx_surface_t gs; 142 memcpy(&gs, _gs, sizeof(gs)); 143 144 for (int py = py0; py < py1; py++) { 145 int y0 = 650 - (1300 * py) / gs.height; 146 for (int px = px0; px < px1; px++) { 147 int x0 = -1250 + (1750 * px) / gs.width; 148 int x = 0, y = 0; 149 for (int i = 0; i < 500; i++) { 150 int x2 = x * x / 1000; 151 int y2 = y * y / 1000; 152 if ((x2 + y2) > 4000) { 153 gs.fgcolor = colors[(i > 11) ? 11 : i]; 154 gfx_plot(&gs, px, py); 155 goto done; 156 } 157 y = 2 * x * y / 1000 + y0; 158 x = x2 - y2 + x0; 159 } 160 gs.fgcolor = 0; 161 gfx_plot(&gs, px, py); 162 done: 163 ; 164 } 165 } 166 167 return 2; 168 } 169 170 int t1(void* arg) { 171 gfx_surface_t *gs = arg; 172 gfx_fill(gs, 0, 0, 320, 240, gfx_color(gs, C_WHITE)); 173 gfx_puts(gs, 0, 240-16, "Thread One"); 174 return render(gs, 0, 0, 320, 240); 175 } 176 int t2(void* arg) { 177 gfx_surface_t *gs = arg; 178 gfx_fill(gs, 320, 240, 640, 480, gfx_color(gs, C_GRAY75)); 179 gfx_puts(gs, 320, 480-16, "Thread Two"); 180 return render(gs, 320, 240, 640, 480); 181 } 182 int t3(void* arg) { 183 gfx_surface_t *gs = arg; 184 gfx_fill(gs, 320, 0, 640, 240, gfx_color(gs, C_GRAY25)); 185 gfx_puts(gs, 320, 240-16, "Thread Three"); 186 return render(gs, 320, 0, 640, 240); 187 } 188 int t4(void* arg) { 189 gfx_surface_t *gs = arg; 190 gfx_fill(gs, 0, 240, 320, 480, gfx_color(gs, C_GRAY50)); 191 gfx_puts(gs, 0, 480-16, "Thread Four"); 192 return render(gs, 0, 240, 320, 480); 193 } 194 195 // program start 196 // ------------- 197 198 #define timer_rd(a) io_rd32(TIMER0_BASE + LX_TIMER_ ## a) 199 #define timer_wr(a,v) io_wr32(TIMER0_BASE + LX_TIMER_ ## a, v) 200 201 void timer_init(void) { 202 // enable timer0 irq 203 csr_set(CSR_S_INTC_ENABLE, TIMER0_IRQb); 204 205 // enable external interrupts 206 csr_set(CSR_SIE, INTb_SVC_EXTERN); 207 208 // program timer for 10ms repeating 209 timer_wr(EN, 0); 210 timer_wr(EV_PENDING, LX_TIMER_EVb_ZERO); 211 timer_wr(LOAD, 0); 212 timer_wr(RELOAD, 50000000/100); 213 timer_wr(EN, 1); 214 timer_wr(EV_ENABLE, LX_TIMER_EVb_ZERO); 215 } 216 217 void interrupt_handler(void) { 218 if (timer_rd(EV_PENDING)) { 219 // acknowledge timer irq 220 timer_wr(EV_PENDING, LX_TIMER_EVb_ZERO); 221 // switch to next thread 222 thread_t* current = threadptr_get(); 223 context_switch(¤t->ctxt, ¤t->next->ctxt); 224 } 225 } 226 227 void exception_handler(eframe_t *ef) { 228 xprintf("\n** SUPERVISOR EXCEPTION **\n"); 229 xprint_s_exception(ef); 230 xprintf("\nHALT\n"); 231 for (;;) ; 232 } 233 234 void start(void) { 235 xprintf("Example 11 - Threads Preemptive\n\n"); 236 237 csr_write(CSR_STVEC, (uintptr_t) trap_entry); 238 239 // setup the main thread (running now) 240 thread_init(); 241 242 // setup graphics 243 gfx_surface_t *gs = alloc(sizeof(*gs)); 244 gfx_init_display(gs); 245 memset((void*) gs->pixels, 0, gs->width * gs->height * 2); 246 247 // launch four worker threads 248 thread_create(t1, gs); 249 thread_create(t2, gs); 250 thread_create(t3, gs); 251 thread_create(t4, gs); 252 253 timer_init(); 254 255 // exit the main thread 256 // this will context switch to another thread 257 // and enable interrupts to allow multitasking 258 thread_exit(0); 259 } 260