mirror of
https://github.com/cccriscv/mini-riscv-os.git
synced 2025-11-16 12:34:33 +00:00
Add the spinlock feature!
This commit is contained in:
39
06-Spinlock/Makefile
Normal file
39
06-Spinlock/Makefile
Normal file
@@ -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
|
||||
133
06-Spinlock/README.md
Normal file
133
06-Spinlock/README.md
Normal file
@@ -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).
|
||||
4
06-Spinlock/gdbinit
Normal file
4
06-Spinlock/gdbinit
Normal file
@@ -0,0 +1,4 @@
|
||||
set disassemble-next-line on
|
||||
b _start
|
||||
target remote : 1234
|
||||
c
|
||||
146
06-Spinlock/lib.c
Normal file
146
06-Spinlock/lib.c
Normal file
@@ -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;
|
||||
}
|
||||
17
06-Spinlock/lib.h
Normal file
17
06-Spinlock/lib.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef __LIB_H__
|
||||
#define __LIB_H__
|
||||
|
||||
#include "riscv.h"
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#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
|
||||
11
06-Spinlock/lock.c
Normal file
11
06-Spinlock/lock.c
Normal file
@@ -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);
|
||||
}
|
||||
32
06-Spinlock/os.c
Normal file
32
06-Spinlock/os.c
Normal file
@@ -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;
|
||||
}
|
||||
15
06-Spinlock/os.h
Normal file
15
06-Spinlock/os.h
Normal file
@@ -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
|
||||
46
06-Spinlock/os.ld
Normal file
46
06-Spinlock/os.ld
Normal file
@@ -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));
|
||||
}
|
||||
116
06-Spinlock/riscv.h
Normal file
116
06-Spinlock/riscv.h
Normal file
@@ -0,0 +1,116 @@
|
||||
#ifndef __RISCV_H__
|
||||
#define __RISCV_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
26
06-Spinlock/start.s
Normal file
26
06-Spinlock/start.s
Normal file
@@ -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
|
||||
8
06-Spinlock/sys.h
Normal file
8
06-Spinlock/sys.h
Normal file
@@ -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
|
||||
145
06-Spinlock/sys.s
Normal file
145
06-Spinlock/sys.s
Normal file
@@ -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
|
||||
|
||||
32
06-Spinlock/task.c
Normal file
32
06-Spinlock/task.c
Normal file
@@ -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);
|
||||
}
|
||||
16
06-Spinlock/task.h
Normal file
16
06-Spinlock/task.h
Normal file
@@ -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
|
||||
38
06-Spinlock/timer.c
Normal file
38
06-Spinlock/timer.c
Normal file
@@ -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;
|
||||
}
|
||||
12
06-Spinlock/timer.h
Normal file
12
06-Spinlock/timer.h
Normal file
@@ -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
|
||||
53
06-Spinlock/trap.c
Normal file
53
06-Spinlock/trap.c
Normal file
@@ -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;
|
||||
}
|
||||
46
06-Spinlock/user.c
Normal file
46
06-Spinlock/user.c
Normal file
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user