From ea5b0a654f02d77d76b810bcf06ded3040d95e33 Mon Sep 17 00:00:00 2001 From: ianchen0119 Date: Thu, 17 Jun 2021 23:00:55 +0800 Subject: [PATCH] Add the spinlock feature! --- 06-Spinlock/Makefile | 39 +++++++++++ 06-Spinlock/README.md | 133 ++++++++++++++++++++++++++++++++++++++ 06-Spinlock/gdbinit | 4 ++ 06-Spinlock/lib.c | 146 ++++++++++++++++++++++++++++++++++++++++++ 06-Spinlock/lib.h | 17 +++++ 06-Spinlock/lock.c | 11 ++++ 06-Spinlock/os.c | 32 +++++++++ 06-Spinlock/os.h | 15 +++++ 06-Spinlock/os.ld | 46 +++++++++++++ 06-Spinlock/riscv.h | 116 +++++++++++++++++++++++++++++++++ 06-Spinlock/start.s | 26 ++++++++ 06-Spinlock/sys.h | 8 +++ 06-Spinlock/sys.s | 145 +++++++++++++++++++++++++++++++++++++++++ 06-Spinlock/task.c | 32 +++++++++ 06-Spinlock/task.h | 16 +++++ 06-Spinlock/timer.c | 38 +++++++++++ 06-Spinlock/timer.h | 12 ++++ 06-Spinlock/trap.c | 53 +++++++++++++++ 06-Spinlock/user.c | 46 +++++++++++++ 19 files changed, 935 insertions(+) create mode 100644 06-Spinlock/Makefile create mode 100644 06-Spinlock/README.md create mode 100644 06-Spinlock/gdbinit create mode 100644 06-Spinlock/lib.c create mode 100644 06-Spinlock/lib.h create mode 100644 06-Spinlock/lock.c create mode 100644 06-Spinlock/os.c create mode 100644 06-Spinlock/os.h create mode 100644 06-Spinlock/os.ld create mode 100644 06-Spinlock/riscv.h create mode 100644 06-Spinlock/start.s create mode 100644 06-Spinlock/sys.h create mode 100644 06-Spinlock/sys.s create mode 100644 06-Spinlock/task.c create mode 100644 06-Spinlock/task.h create mode 100644 06-Spinlock/timer.c create mode 100644 06-Spinlock/timer.h create mode 100644 06-Spinlock/trap.c create mode 100644 06-Spinlock/user.c diff --git a/06-Spinlock/Makefile b/06-Spinlock/Makefile new file mode 100644 index 0000000..e371024 --- /dev/null +++ b/06-Spinlock/Makefile @@ -0,0 +1,39 @@ +CC = riscv64-unknown-elf-gcc +CFLAGS = -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 +GDB = riscv64-unknown-elf-gdb + +OBJ = \ +start.s \ +sys.s \ +lib.c \ +timer.c \ +task.c \ +os.c \ +user.c \ +trap.c \ +lock.c + +QEMU = qemu-system-riscv32 +QFLAGS = -nographic -smp 4 -machine virt -bios none + +OBJDUMP = riscv64-unknown-elf-objdump + +all: os.elf + +os.elf: $(OBJ) + $(CC) $(CFLAGS) -g -Wall -T os.ld -o os.elf $^ + +qemu: $(TARGET) + @qemu-system-riscv32 -M ? | grep virt >/dev/null || exit + @echo "Press Ctrl-A and then X to exit QEMU" + $(QEMU) $(QFLAGS) -kernel os.elf + +clean: + rm -f *.elf + +.PHONY : debug +debug: all + @echo "Press Ctrl-C and then input 'quit' to exit GDB and QEMU" + @echo "-------------------------------------------------------" + @${QEMU} ${QFLAGS} -kernel os.elf -s -S & + @${GDB} os.elf -q -x ./gdbinit \ No newline at end of file diff --git a/06-Spinlock/README.md b/06-Spinlock/README.md new file mode 100644 index 0000000..d937b33 --- /dev/null +++ b/06-Spinlock/README.md @@ -0,0 +1,133 @@ +# 06-Spinlock + +## Build & Run + +```sh +IAN@DESKTOP-9AEMEPL MINGW64 ~/Desktop/mini-riscv-os/06-Spinlock (feat/spinlock) +$ make +riscv64-unknown-elf-gcc -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall -T os.ld -o os.elf start.s sys.s lib.c timer.c task.c os.c user.c trap.c lock.c + +IAN@DESKTOP-9AEMEPL MINGW64 ~/Desktop/mini-riscv-os/06-Spinlock (feat/spinlock) +$ make qemu +Press Ctrl-A and then X to exit QEMU +qemu-system-riscv32 -nographic -smp 4 -machine virt -bios none -kernel os.elf +OS start +OS: Activate next task +Task0: Created! +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +timer interruption! +timer_handler: 1 +OS: Back to OS + +OS: Activate next task +Task1: Created! +Task1: Running... +Task1: Running... +Task1: Running... +Task1: Running... +Task1: Running... +Task1: Running... +Task1: Running... +Task1: Running... +Task1: Running... +Task1: Running... +Task1: Running... +Task1: Running... +Task1: Running... +Task1: Running... +timer interruption! +timer_handler: 2 +OS: Back to OS + +OS: Activate next task +Task2: Created! +The value of shared_var is: 550 +The value of shared_var is: 600 +The value of shared_var is: 650 +The value of shared_var is: 700 +The value of shared_var is: 750 +The value of shared_var is: 800 +The value of shared_var is: 850 +The value of shared_var is: 900 +The value of shared_var is: 950 +The value of shared_var is: 1000 +The value of shared_var is: 1050 +The value of shared_var is: 1100 +The value of shared_var is: 1150 +The value of shared_var is: 1200 +The value of shared_var is: 1250 +The value of shared_var is: 1300 +The value of shared_var is: 1350 +The value of shared_var is: 1400 +The value of shared_var is: 1450 +The value of shared_var is: 1500 +The value of shared_var is: 1550 +The value of shared_var is: 1600 +timer interruption! +timer_handler: 3 +OS: Back to OS + +OS: Activate next task +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +Task0: Running... +timer interruption! +timer_handler: 4 +OS: Back to OS +QEMU: Terminated +``` + +## Debug mode + +```sh +make debug +riscv64-unknown-elf-gcc -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall -T os.ld -o os.elf start.s sys.s lib.c timer.c task.c os.c user.c trap.c lock.c +Press Ctrl-C and then input 'quit' to exit GDB and QEMU +------------------------------------------------------- +Reading symbols from os.elf... +Breakpoint 1 at 0x80000000: file start.s, line 7. +0x00001000 in ?? () +=> 0x00001000: 97 02 00 00 auipc t0,0x0 + +Thread 1 hit Breakpoint 1, _start () at start.s:7 +7 csrr t0, mhartid # read current hart id +=> 0x80000000 <_start+0>: f3 22 40 f1 csrr t0,mhartid +(gdb) +``` + +### set the breakpoint + +You can set the breakpoint in any c file: + +```sh +(gdb) b trap.c:27 +Breakpoint 2 at 0x80008f78: file trap.c, line 27. +(gdb) +``` + +As the example above, when process running on trap.c, line 27 (Timer Interrupt). +The process will be suspended automatically until you press the key `c` (continue) or `s` (step). diff --git a/06-Spinlock/gdbinit b/06-Spinlock/gdbinit new file mode 100644 index 0000000..7ca6884 --- /dev/null +++ b/06-Spinlock/gdbinit @@ -0,0 +1,4 @@ +set disassemble-next-line on +b _start +target remote : 1234 +c \ No newline at end of file diff --git a/06-Spinlock/lib.c b/06-Spinlock/lib.c new file mode 100644 index 0000000..3538b9d --- /dev/null +++ b/06-Spinlock/lib.c @@ -0,0 +1,146 @@ +#include "lib.h" + +void lib_delay(volatile int count) +{ + count *= 50000; + while (count--); +} + +int lib_putc(char ch) { + while ((*UART_LSR & UART_LSR_EMPTY_MASK) == 0); + return *UART_THR = ch; +} + +void lib_puts(char *s) { + while (*s) lib_putc(*s++); +} + + +int lib_vsnprintf(char * out, size_t n, const char* s, va_list vl) +{ + int format = 0; + int longarg = 0; + size_t pos = 0; + for( ; *s; s++) { + if (format) { + switch(*s) { + case 'l': { + longarg = 1; + break; + } + case 'p': { + longarg = 1; + if (out && pos < n) { + out[pos] = '0'; + } + pos++; + if (out && pos < n) { + out[pos] = 'x'; + } + pos++; + } + case 'x': { + long num = longarg ? va_arg(vl, long) : va_arg(vl, int); + int hexdigits = 2*(longarg ? sizeof(long) : sizeof(int))-1; + for(int i = hexdigits; i >= 0; i--) { + int d = (num >> (4*i)) & 0xF; + if (out && pos < n) { + out[pos] = (d < 10 ? '0'+d : 'a'+d-10); + } + pos++; + } + longarg = 0; + format = 0; + break; + } + case 'd': { + long num = longarg ? va_arg(vl, long) : va_arg(vl, int); + if (num < 0) { + num = -num; + if (out && pos < n) { + out[pos] = '-'; + } + pos++; + } + long digits = 1; + for (long nn = num; nn /= 10; digits++) + ; + for (int i = digits-1; i >= 0; i--) { + if (out && pos + i < n) { + out[pos + i] = '0' + (num % 10); + } + num /= 10; + } + pos += digits; + longarg = 0; + format = 0; + break; + } + case 's': { + const char* s2 = va_arg(vl, const char*); + while (*s2) { + if (out && pos < n) { + out[pos] = *s2; + } + pos++; + s2++; + } + longarg = 0; + format = 0; + break; + } + case 'c': { + if (out && pos < n) { + out[pos] = (char)va_arg(vl,int); + } + pos++; + longarg = 0; + format = 0; + break; + } + default: + break; + } + } + else if(*s == '%') { + format = 1; + } + else { + if (out && pos < n) { + out[pos] = *s; + } + pos++; + } + } + if (out && pos < n) { + out[pos] = 0; + } + else if (out && n) { + out[n-1] = 0; + } + return pos; +} + +static char out_buf[1000]; // buffer for lib_vprintf() + +int lib_vprintf(const char* s, va_list vl) +{ + int res = lib_vsnprintf(NULL, -1, s, vl); + if (res+1 >= sizeof(out_buf)) { + lib_puts("error: lib_vprintf() output string size overflow\n"); + while(1) {} + } + lib_vsnprintf(out_buf, res + 1, s, vl); + lib_puts(out_buf); + return res; +} + +int lib_printf(const char* s, ...) +{ + int res = 0; + va_list vl; + va_start(vl, s); + res = lib_vprintf(s, vl); + va_end(vl); + return res; +} diff --git a/06-Spinlock/lib.h b/06-Spinlock/lib.h new file mode 100644 index 0000000..e338c8e --- /dev/null +++ b/06-Spinlock/lib.h @@ -0,0 +1,17 @@ +#ifndef __LIB_H__ +#define __LIB_H__ + +#include "riscv.h" +#include +#include + +#define lib_error(...) { lib_printf(__VA_ARGS__); while(1) {} } } + +extern void lib_delay(volatile int count); +extern int lib_putc(char ch); +extern void lib_puts(char *s); +extern int lib_printf(const char* s, ...); +extern int lib_vprintf(const char* s, va_list vl); +extern int lib_vsnprintf(char * out, size_t n, const char* s, va_list vl); + +#endif diff --git a/06-Spinlock/lock.c b/06-Spinlock/lock.c new file mode 100644 index 0000000..c25c2a5 --- /dev/null +++ b/06-Spinlock/lock.c @@ -0,0 +1,11 @@ +#include "os.h" + +void spinlock_lock() +{ + w_mstatus(r_mstatus() & ~MSTATUS_MIE); +} + +void spinlock_unlock() +{ + w_mstatus(r_mstatus() | MSTATUS_MIE); +} \ No newline at end of file diff --git a/06-Spinlock/os.c b/06-Spinlock/os.c new file mode 100644 index 0000000..09d957d --- /dev/null +++ b/06-Spinlock/os.c @@ -0,0 +1,32 @@ +#include "os.h" + +extern void trap_init(void); + +void os_kernel() +{ + task_os(); +} + +void os_start() +{ + lib_puts("OS start\n"); + user_init(); + trap_init(); + timer_init(); // start timer interrupt ... +} + +int os_main(void) +{ + os_start(); + + int current_task = 0; + while (1) + { + lib_puts("OS: Activate next task\n"); + task_go(current_task); + lib_puts("OS: Back to OS\n"); + current_task = (current_task + 1) % taskTop; // Round Robin Scheduling + lib_puts("\n"); + } + return 0; +} diff --git a/06-Spinlock/os.h b/06-Spinlock/os.h new file mode 100644 index 0000000..7a0c9c4 --- /dev/null +++ b/06-Spinlock/os.h @@ -0,0 +1,15 @@ +#ifndef __OS_H__ +#define __OS_H__ + +#include "riscv.h" +#include "lib.h" +#include "task.h" +#include "timer.h" + +extern void user_init(); +extern void os_kernel(); +extern int os_main(void); +extern void spinlock_lock(); +extern void spinlock_unlock(); + +#endif diff --git a/06-Spinlock/os.ld b/06-Spinlock/os.ld new file mode 100644 index 0000000..9c1a7df --- /dev/null +++ b/06-Spinlock/os.ld @@ -0,0 +1,46 @@ +OUTPUT_ARCH( "riscv" ) + +ENTRY( _start ) + +MEMORY +{ + ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M +} + +PHDRS +{ + text PT_LOAD; + data PT_LOAD; + bss PT_LOAD; +} + +SECTIONS +{ + .text : { + PROVIDE(_text_start = .); + *(.text.init) *(.text .text.*) + PROVIDE(_text_end = .); + } >ram AT>ram :text + + .rodata : { + PROVIDE(_rodata_start = .); + *(.rodata .rodata.*) + PROVIDE(_rodata_end = .); + } >ram AT>ram :text + + .data : { + . = ALIGN(4096); + PROVIDE(_data_start = .); + *(.sdata .sdata.*) *(.data .data.*) + PROVIDE(_data_end = .); + } >ram AT>ram :data + + .bss :{ + PROVIDE(_bss_start = .); + *(.sbss .sbss.*) *(.bss .bss.*) + PROVIDE(_bss_end = .); + } >ram AT>ram :bss + + PROVIDE(_memory_start = ORIGIN(ram)); + PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram)); +} diff --git a/06-Spinlock/riscv.h b/06-Spinlock/riscv.h new file mode 100644 index 0000000..27277bb --- /dev/null +++ b/06-Spinlock/riscv.h @@ -0,0 +1,116 @@ +#ifndef __RISCV_H__ +#define __RISCV_H__ + +#include + +#define reg_t uint32_t // RISCV32: register is 32bits +// define reg_t as uint64_t // RISCV64: register is 64bits + +// ref: https://www.activexperts.com/serial-port-component/tutorials/uart/ +#define UART 0x10000000 +#define UART_THR (uint8_t*)(UART+0x00) // THR:transmitter holding register +#define UART_LSR (uint8_t*)(UART+0x05) // LSR:line status register +#define UART_LSR_EMPTY_MASK 0x40 // LSR Bit 6: Transmitter empty; both the THR and LSR are empty + +// Saved registers for kernel context switches. +struct context { + reg_t ra; + reg_t sp; + + // callee-saved + reg_t s0; + reg_t s1; + reg_t s2; + reg_t s3; + reg_t s4; + reg_t s5; + reg_t s6; + reg_t s7; + reg_t s8; + reg_t s9; + reg_t s10; + reg_t s11; +}; + +// ref: https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/riscv.h +// +// local interrupt controller, which contains the timer. +// ================== Timer Interrput ==================== + +#define NCPU 8 // maximum number of CPUs +#define CLINT 0x2000000 +#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 4*(hartid)) +#define CLINT_MTIME (CLINT + 0xBFF8) // cycles since boot. + +// which hart (core) is this? +static inline reg_t r_mhartid() +{ + reg_t x; + asm volatile("csrr %0, mhartid" : "=r" (x) ); + return x; +} + +// Machine Status Register, mstatus +#define MSTATUS_MPP_MASK (3 << 11) // previous mode. +#define MSTATUS_MPP_M (3 << 11) +#define MSTATUS_MPP_S (1 << 11) +#define MSTATUS_MPP_U (0 << 11) +#define MSTATUS_MIE (1 << 3) // machine-mode interrupt enable. + +static inline reg_t r_mstatus() +{ + reg_t x; + asm volatile("csrr %0, mstatus" : "=r" (x) ); + return x; +} + +static inline void w_mstatus(reg_t x) +{ + asm volatile("csrw mstatus, %0" : : "r" (x)); +} + +// machine exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void w_mepc(reg_t x) +{ + asm volatile("csrw mepc, %0" : : "r" (x)); +} + +static inline reg_t r_mepc() +{ + reg_t x; + asm volatile("csrr %0, mepc" : "=r" (x)); + return x; +} + +// Machine Scratch register, for early trap handler +static inline void w_mscratch(reg_t x) +{ + asm volatile("csrw mscratch, %0" : : "r" (x)); +} + +// Machine-mode interrupt vector +static inline void w_mtvec(reg_t x) +{ + asm volatile("csrw mtvec, %0" : : "r" (x)); +} + +// Machine-mode Interrupt Enable +#define MIE_MEIE (1 << 11) // external +#define MIE_MTIE (1 << 7) // timer +#define MIE_MSIE (1 << 3) // software + +static inline reg_t r_mie() +{ + reg_t x; + asm volatile("csrr %0, mie" : "=r" (x) ); + return x; +} + +static inline void w_mie(reg_t x) +{ + asm volatile("csrw mie, %0" : : "r" (x)); +} + +#endif diff --git a/06-Spinlock/start.s b/06-Spinlock/start.s new file mode 100644 index 0000000..aec0240 --- /dev/null +++ b/06-Spinlock/start.s @@ -0,0 +1,26 @@ +.equ STACK_SIZE, 8192 + +.global _start + +_start: + # setup stacks per hart + csrr t0, mhartid # read current hart id + slli t0, t0, 10 # shift left the hart id by 1024 + la sp, stacks + STACK_SIZE # set the initial stack pointer + # to the end of the stack space + add sp, sp, t0 # move the current hart stack pointer + # to its place in the stack space + + # park harts with id != 0 + csrr a0, mhartid # read current hart id + bnez a0, park # if we're not on the hart 0 + # we park the hart + + j os_main # hart 0 jump to c + +park: + wfi + j park + +stacks: + .skip STACK_SIZE * 4 # allocate space for the harts stacks diff --git a/06-Spinlock/sys.h b/06-Spinlock/sys.h new file mode 100644 index 0000000..4355ab3 --- /dev/null +++ b/06-Spinlock/sys.h @@ -0,0 +1,8 @@ +#ifndef __SYS_H__ +#define __SYS_H__ + +#include "riscv.h" +extern void sys_timer(); +extern void sys_switch(struct context *ctx_old, struct context *ctx_new); + +#endif diff --git a/06-Spinlock/sys.s b/06-Spinlock/sys.s new file mode 100644 index 0000000..0c6964e --- /dev/null +++ b/06-Spinlock/sys.s @@ -0,0 +1,145 @@ +# This Code derived from xv6-riscv (64bit) +# -- https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/swtch.S + +# ============ MACRO ================== +.macro ctx_save base + sw ra, 0(\base) + sw sp, 4(\base) + sw s0, 8(\base) + sw s1, 12(\base) + sw s2, 16(\base) + sw s3, 20(\base) + sw s4, 24(\base) + sw s5, 28(\base) + sw s6, 32(\base) + sw s7, 36(\base) + sw s8, 40(\base) + sw s9, 44(\base) + sw s10, 48(\base) + sw s11, 52(\base) +.endm + +.macro ctx_load base + lw ra, 0(\base) + lw sp, 4(\base) + lw s0, 8(\base) + lw s1, 12(\base) + lw s2, 16(\base) + lw s3, 20(\base) + lw s4, 24(\base) + lw s5, 28(\base) + lw s6, 32(\base) + lw s7, 36(\base) + lw s8, 40(\base) + lw s9, 44(\base) + lw s10, 48(\base) + lw s11, 52(\base) +.endm + +.macro reg_save base + # save the registers. + sw ra, 0(\base) + sw sp, 4(\base) + sw gp, 8(\base) + sw tp, 12(\base) + sw t0, 16(\base) + sw t1, 20(\base) + sw t2, 24(\base) + sw s0, 28(\base) + sw s1, 32(\base) + sw a0, 36(\base) + sw a1, 40(\base) + sw a2, 44(\base) + sw a3, 48(\base) + sw a4, 52(\base) + sw a5, 56(\base) + sw a6, 60(\base) + sw a7, 64(\base) + sw s2, 68(\base) + sw s3, 72(\base) + sw s4, 76(\base) + sw s5, 80(\base) + sw s6, 84(\base) + sw s7, 88(\base) + sw s8, 92(\base) + sw s9, 96(\base) + sw s10, 100(\base) + sw s11, 104(\base) + sw t3, 108(\base) + sw t4, 112(\base) + sw t5, 116(\base) + sw t6, 120(\base) +.endm + +.macro reg_load base + # restore registers. + lw ra, 0(\base) + lw sp, 4(\base) + lw gp, 8(\base) + # not this, in case we moved CPUs: lw tp, 12(\base) + lw t0, 16(\base) + lw t1, 20(\base) + lw t2, 24(\base) + lw s0, 28(\base) + lw s1, 32(\base) + lw a0, 36(\base) + lw a1, 40(\base) + lw a2, 44(\base) + lw a3, 48(\base) + lw a4, 52(\base) + lw a5, 56(\base) + lw a6, 60(\base) + lw a7, 64(\base) + lw s2, 68(\base) + lw s3, 72(\base) + lw s4, 76(\base) + lw s5, 80(\base) + lw s6, 84(\base) + lw s7, 88(\base) + lw s8, 92(\base) + lw s9, 96(\base) + lw s10, 100(\base) + lw s11, 104(\base) + lw t3, 108(\base) + lw t4, 112(\base) + lw t5, 116(\base) + lw t6, 120(\base) +.endm +# ============ Macro END ================== + +# Context switch +# +# void sys_switch(struct context *old, struct context *new); +# +# Save current registers in old. Load from new. + +.globl sys_switch +.align 4 +sys_switch: + + ctx_save a0 # a0 => struct context *old + ctx_load a1 # a1 => struct context *new + ret # pc=ra; swtch to new task (new->ra) + + +.globl trap_vector +# the trap vector base address must always be aligned on a 4-byte boundary +.align 4 +trap_vector: + # save context(registers). + csrrw t6, mscratch, t6 # swap t6 and mscratch + reg_save t6 + csrw mscratch, t6 + # call the C trap handler in trap.c + csrr a0, mepc + csrr a1, mcause + call trap_handler + + # trap_handler will return the return address via a0. + csrw mepc, a0 + + # load context(registers). + csrr t6, mscratch + reg_load t6 + mret + diff --git a/06-Spinlock/task.c b/06-Spinlock/task.c new file mode 100644 index 0000000..17a519e --- /dev/null +++ b/06-Spinlock/task.c @@ -0,0 +1,32 @@ +#include "task.h" +#include "lib.h" + +uint8_t task_stack[MAX_TASK][STACK_SIZE]; +struct context ctx_os; +struct context ctx_tasks[MAX_TASK]; +struct context *ctx_now; +int taskTop = 0; // total number of task + +// create a new task +int task_create(void (*task)(void)) +{ + int i = taskTop++; + ctx_tasks[i].ra = (reg_t)task; + ctx_tasks[i].sp = (reg_t)&task_stack[i][STACK_SIZE - 1]; + return i; +} + +// switch to task[i] +void task_go(int i) +{ + ctx_now = &ctx_tasks[i]; + sys_switch(&ctx_os, &ctx_tasks[i]); +} + +// switch back to os +void task_os() +{ + struct context *ctx = ctx_now; + ctx_now = &ctx_os; + sys_switch(ctx, &ctx_os); +} diff --git a/06-Spinlock/task.h b/06-Spinlock/task.h new file mode 100644 index 0000000..1ac7e39 --- /dev/null +++ b/06-Spinlock/task.h @@ -0,0 +1,16 @@ +#ifndef __TASK_H__ +#define __TASK_H__ + +#include "riscv.h" +#include "sys.h" + +#define MAX_TASK 10 +#define STACK_SIZE 1024 + +extern int taskTop; + +extern int task_create(void (*task)(void)); +extern void task_go(int i); +extern void task_os(); + +#endif diff --git a/06-Spinlock/timer.c b/06-Spinlock/timer.c new file mode 100644 index 0000000..b2dc42c --- /dev/null +++ b/06-Spinlock/timer.c @@ -0,0 +1,38 @@ +#include "timer.h" + +// a scratch area per CPU for machine-mode timer interrupts. +reg_t timer_scratch[NCPU][5]; + +#define interval 20000000 // cycles; about 2 second in qemu. + +void timer_init() +{ + // each CPU has a separate source of timer interrupts. + int id = r_mhartid(); + + // ask the CLINT for a timer interrupt. + // int interval = 1000000; // cycles; about 1/10th second in qemu. + + *(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval; + + // prepare information in scratch[] for timervec. + // scratch[0..2] : space for timervec to save registers. + // scratch[3] : address of CLINT MTIMECMP register. + // scratch[4] : desired interval (in cycles) between timer interrupts. + reg_t *scratch = &timer_scratch[id][0]; + scratch[3] = CLINT_MTIMECMP(id); + scratch[4] = interval; + w_mscratch((reg_t)scratch); + + // enable machine-mode timer interrupts. + w_mie(r_mie() | MIE_MTIE); +} + +static int timer_count = 0; + +void timer_handler() +{ + lib_printf("timer_handler: %d\n", ++timer_count); + int id = r_mhartid(); + *(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval; +} diff --git a/06-Spinlock/timer.h b/06-Spinlock/timer.h new file mode 100644 index 0000000..b971c37 --- /dev/null +++ b/06-Spinlock/timer.h @@ -0,0 +1,12 @@ +#ifndef __TIMER_H__ +#define __TIMER_H__ + +#include "riscv.h" +#include "sys.h" +#include "lib.h" +#include "task.h" + +extern void timer_handler(); +extern void timer_init(); + +#endif diff --git a/06-Spinlock/trap.c b/06-Spinlock/trap.c new file mode 100644 index 0000000..54974f8 --- /dev/null +++ b/06-Spinlock/trap.c @@ -0,0 +1,53 @@ +#include "os.h" +extern void trap_vector(); + +void trap_init() +{ + // set the machine-mode trap handler. + w_mtvec((reg_t)trap_vector); + + // enable machine-mode interrupts. + w_mstatus(r_mstatus() | MSTATUS_MIE); +} + +reg_t trap_handler(reg_t epc, reg_t cause) +{ + reg_t return_pc = epc; + reg_t cause_code = cause & 0xfff; + + if (cause & 0x80000000) + { + /* Asynchronous trap - interrupt */ + switch (cause_code) + { + case 3: + lib_puts("software interruption!\n"); + break; + case 7: + lib_puts("timer interruption!\n"); + // disable machine-mode timer interrupts. + w_mie(~((~r_mie()) | (1 << 7))); + timer_handler(); + return_pc = (reg_t)&os_kernel; + // enable machine-mode timer interrupts. + w_mie(r_mie() | MIE_MTIE); + break; + case 11: + lib_puts("external interruption!\n"); + break; + default: + lib_puts("unknown async exception!\n"); + break; + } + } + else + { + /* Synchronous trap - exception */ + lib_puts("Sync exceptions!\n"); + while (1) + { + /* code */ + } + } + return return_pc; +} diff --git a/06-Spinlock/user.c b/06-Spinlock/user.c new file mode 100644 index 0000000..5754b55 --- /dev/null +++ b/06-Spinlock/user.c @@ -0,0 +1,46 @@ +#include "os.h" + +int shared_var = 500; + +void user_task0(void) +{ + lib_puts("Task0: Created!\n"); + while (1) + { + lib_puts("Task0: Running...\n"); + lib_delay(1000); + } +} + +void user_task1(void) +{ + lib_puts("Task1: Created!\n"); + while (1) + { + lib_puts("Task1: Running...\n"); + lib_delay(1000); + } +} + +void user_task2(void) +{ + lib_puts("Task2: Created!\n"); + while (1) + { + for (int i = 0; i < 50; i++) + { + spinlock_lock(); + shared_var++; + spinlock_unlock(); + } + lib_delay(6000); + lib_printf("The value of shared_var is: %d \n", shared_var); + } +} + +void user_init() +{ + task_create(&user_task0); + task_create(&user_task1); + task_create(&user_task2); +}