os-workshop

same materials and sample source for RV32 OS projects
git clone http://frotz.net/git/os-workshop.git
Log | Files | Refs

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(&current->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(&current->ctxt, &current->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