|
| 1 | +<!-- 1385f545-517d-450c-a7c8-44bb3f38e523 5ba95bfc-3710-4aa8-830e-0628fb274a0f --> |
| 2 | +# uROS RISC-V Mini-OS Implementation |
| 3 | + |
| 4 | +## Architecture Overview |
| 5 | + |
| 6 | +- **Platform**: QEMU virt, RISC-V 64 (rv64gc), S-mode bare metal |
| 7 | +- **Base address**: 0x80200000 (linker script) |
| 8 | +- **Boot**: OpenSBI (-bios default) |
| 9 | +- **Timer**: SBI set_timer, 100 Hz tickrate (~10ms) |
| 10 | +- **Scheduler**: RR preemptive (quantum=5 ticks) + SJF non-preemptive |
| 11 | +- **Tasks**: Kernel threads with context switching |
| 12 | + |
| 13 | +## File Structure |
| 14 | + |
| 15 | +``` |
| 16 | +/Users/thom/Library/Mobile Documents/com~apple~CloudDocs/Universidad/OS/mini-os/ |
| 17 | +├── Makefile |
| 18 | +├── linker.ld |
| 19 | +├── boot/start.S |
| 20 | +├── kernel/kmain.c |
| 21 | +├── kernel/trap.c |
| 22 | +├── kernel/task.c |
| 23 | +├── kernel/sched.c |
| 24 | +├── kernel/shell.c |
| 25 | +├── drivers/uart.c |
| 26 | +├── drivers/timer.c |
| 27 | +├── lib/printf.c |
| 28 | +├── include/uros.h |
| 29 | +├── scripts/run-qemu.sh |
| 30 | +├── scripts/demo.sh |
| 31 | +└── docs/README.md |
| 32 | +``` |
| 33 | + |
| 34 | +## Implementation Steps |
| 35 | + |
| 36 | +### 1. Core Infrastructure & Headers |
| 37 | + |
| 38 | +**File: `include/uros.h`** |
| 39 | + |
| 40 | +- Define basic types (u8, u16, u32, u64, size_t) |
| 41 | +- Define `context_t` struct (x1-x31, sstatus, sepc) |
| 42 | +- Define `pcb_t` struct with: pid, state (NEW/READY/RUNNING/SLEEPING/ZOMBIE), context, stack pointer, entry function, ticks_used, burst_hint, burst_estimate, arrival_time, finish_time, wait_time |
| 43 | +- Prototypes for all subsystems: uart, timer, trap, task, sched, shell, printf |
| 44 | +- SBI call interface prototype |
| 45 | +- Constants: UART_BASE=0x10000000, TICK_HZ=100, RR_QUANTUM=5 |
| 46 | + |
| 47 | +### 2. Linker Script & Boot Assembly |
| 48 | + |
| 49 | +**File: `linker.ld`** |
| 50 | + |
| 51 | +- BASE = 0x80200000 |
| 52 | +- Sections: .text, .rodata, .data, .bss |
| 53 | +- PROVIDE(_stack_top) for main kernel stack |
| 54 | + |
| 55 | +**File: `boot/start.S`** |
| 56 | + |
| 57 | +- Entry point: set sp to _stack_top |
| 58 | +- Jump to kmain |
| 59 | +- **ctx_switch(from, to)** implementation: |
| 60 | + - Save x1-x31, sstatus, sepc to from->context |
| 61 | + - Restore x1-x31, sstatus, sepc from to->context |
| 62 | + - Use register pairs: sd/ld with offset calculations |
| 63 | + - Handle sp specially (x2) |
| 64 | + |
| 65 | +### 3. UART Driver (NS16550A) |
| 66 | + |
| 67 | +**File: `drivers/uart.c`** |
| 68 | + |
| 69 | +- MMIO base 0x10000000 |
| 70 | +- Registers: THR (0), RBR (0), LSR (5), IER (1), LCR (3) |
| 71 | +- `uart_init()`: configure 8N1, disable interrupts |
| 72 | +- `uart_putc(char c)`: wait for THRE bit in LSR, write to THR |
| 73 | +- `uart_getc()`: poll RBR with timeout |
| 74 | +- `uart_puts(const char *s)`: loop uart_putc |
| 75 | +- Input buffering with echo and backspace support |
| 76 | + |
| 77 | +### 4. Printf Implementation |
| 78 | + |
| 79 | +**File: `lib/printf.c`** |
| 80 | + |
| 81 | +- `kprintf(const char *fmt, ...)` with va_args |
| 82 | +- Support: %s, %d, %u, %x, %c |
| 83 | +- Use uart_putc for output |
| 84 | +- Simple number-to-string conversion helpers |
| 85 | + |
| 86 | +### 5. Timer Driver (SBI) |
| 87 | + |
| 88 | +**File: `drivers/timer.c`** |
| 89 | + |
| 90 | +- `sbi_call(eid, fid, a0-a5)`: inline assembly for ecall |
| 91 | +- `sbi_set_timer(u64 stime_value)`: EID=0, FID=0 |
| 92 | +- `rdtime()`: read CSR time with inline asm |
| 93 | +- `timer_init(hz)`: calculate tick_delta = TIMEBASE_FREQ / hz (assume 10MHz) |
| 94 | +- `timer_schedule_next()`: rdtime() + tick_delta, call sbi_set_timer |
| 95 | +- Global g_ticks counter |
| 96 | + |
| 97 | +### 6. Trap Handling |
| 98 | + |
| 99 | +**File: `kernel/trap.c`** |
| 100 | + |
| 101 | +- `trap_init()`: |
| 102 | + - Set stvec to trap_handler address (direct mode) |
| 103 | + - Enable SIE bit in sstatus: `sstatus |= (1 << 1)` |
| 104 | + - Enable STIE bit in sie: `sie |= (1 << 5)` |
| 105 | +- `trap_handler()`: read scause |
| 106 | + - If supervisor timer interrupt (bit 63 set, code 5): |
| 107 | + - Call timer_schedule_next() |
| 108 | + - Increment g_ticks |
| 109 | + - Call sched_on_tick() |
| 110 | + - Handle other traps (print and halt) |
| 111 | + |
| 112 | +### 7. Task Management |
| 113 | + |
| 114 | +**File: `kernel/task.c`** |
| 115 | + |
| 116 | +- Bump allocator: static heap array, `kmalloc(size)` advances pointer |
| 117 | +- Task table: MAX_TASKS=32, array of pcb_t |
| 118 | +- `task_create(void (*entry)(void*), void *arg, int burst_hint)`: |
| 119 | + - Allocate PID, stack (4KB per task) |
| 120 | + - Initialize context: set sepc to entry, sp to stack_top |
| 121 | + - Set burst_hint and burst_estimate |
| 122 | + - Set arrival_time = g_ticks |
| 123 | + - State = NEW → READY, add to scheduler |
| 124 | +- `task_exit()`: mark ZOMBIE, yield |
| 125 | +- `yield()`: call sched_yield() which does ctx_switch |
| 126 | +- `task_get_by_pid(pid)`: lookup helper |
| 127 | +- Track memory usage for meminfo |
| 128 | + |
| 129 | +### 8. Scheduler (RR + SJF) |
| 130 | + |
| 131 | +**File: `kernel/sched.c`** |
| 132 | + |
| 133 | +- Mode enum: SCHED_RR, SCHED_SJF |
| 134 | +- Global: current_mode, current_task, ready_queue |
| 135 | +- **Round-Robin (preemptive)**: |
| 136 | + - `sched_on_tick()`: if mode==RR, decrement quantum; if 0, yield |
| 137 | + - `sched_pick_next()`: FIFO from ready_queue |
| 138 | +- **SJF (non-preemptive)**: |
| 139 | + - `sched_pick_next()`: sort/find task with min burst_estimate |
| 140 | + - Tie-break: arrival_time (FCFS) or PID |
| 141 | + - Update estimate after task completes: τ_new = 0.5 * real + 0.5 * τ_old |
| 142 | +- `sched_set_mode(mode)`: switch between RR/SJF |
| 143 | +- `sched_yield()`: ctx_switch to next task |
| 144 | +- Track wait_time, finish_time for metrics |
| 145 | + |
| 146 | +### 9. Shell & Commands |
| 147 | + |
| 148 | +**File: `kernel/shell.c`** |
| 149 | + |
| 150 | +- `shell_run()`: infinite loop reading lines from UART |
| 151 | +- Parse command and dispatch: |
| 152 | + - **help**: list all commands |
| 153 | + - **ps**: print PID, state, ticks_used, burst_estimate for all tasks |
| 154 | + - **run cpu**: create CPU-bound task (loop with occasional yield) |
| 155 | + - **run io**: create I/O task (sleep 5 ticks between prints) |
| 156 | + - **kill \<pid\>**: mark task as ZOMBIE |
| 157 | + - **sched rr|sjf**: call sched_set_mode() |
| 158 | + - **bench**: run benchmark (see below) |
| 159 | + - **uptime**: print g_ticks / 100.0 seconds |
| 160 | + - **meminfo**: print bump allocator usage, stack memory |
| 161 | + |
| 162 | +**Benchmark implementation**: |
| 163 | + |
| 164 | +- Create 6-10 tasks with known burst_hints (e.g., 10, 20, 15, 30, 25, 12 ticks) |
| 165 | +- **Round 1 (RR)**: |
| 166 | + - Set mode to RR |
| 167 | + - Create tasks, wait until all ZOMBIE |
| 168 | + - Collect: wait_time, turnaround (finish - arrival), total duration |
| 169 | +- **Round 2 (SJF)**: |
| 170 | + - Reset task table |
| 171 | + - Set mode to SJF |
| 172 | + - Create same tasks (reset arrival times) |
| 173 | + - Wait until all ZOMBIE |
| 174 | + - Collect same metrics |
| 175 | +- Print comparison table: |
| 176 | + ``` |
| 177 | + Benchmark Results: |
| 178 | + RR SJF |
| 179 | + Wait: X.XX Y.YY ticks |
| 180 | + T.around: X.XX Y.YY ticks |
| 181 | + T.put: X.XX Y.YY tasks/sec |
| 182 | + ``` |
| 183 | + |
| 184 | + |
| 185 | +### 10. Kernel Main |
| 186 | + |
| 187 | +**File: `kernel/kmain.c`** |
| 188 | + |
| 189 | +- Call uart_init() |
| 190 | +- Call trap_init() |
| 191 | +- Call timer_init(100) |
| 192 | +- Call sched_init() |
| 193 | +- Print banner: "uROS (rv64gc, QEMU virt) - console ready\nticks=0\n" |
| 194 | +- Create 1-2 demo tasks (optional) |
| 195 | +- Call shell_run() (never returns) |
| 196 | + |
| 197 | +### 11. Build System |
| 198 | + |
| 199 | +**File: `Makefile`** |
| 200 | + |
| 201 | +- Toolchain: CROSS=riscv64-unknown-elf- |
| 202 | +- CFLAGS: -march=rv64gc -mabi=lp64 -ffreestanding -nostdlib -nostartfiles -Wall -Wextra -O2 -I include |
| 203 | +- LDFLAGS: -T linker.ld -nostdlib |
| 204 | +- Targets: |
| 205 | + - **all**: compile all .c and .S files, link to build/kernel.elf, generate kernel.map |
| 206 | + - **run**: execute scripts/run-qemu.sh |
| 207 | + - **clean**: rm -rf build/ |
| 208 | + - **dtb**: dump QEMU DTB and convert to DTS with dtc |
| 209 | + |
| 210 | +**File: `scripts/run-qemu.sh`** |
| 211 | + |
| 212 | +```bash |
| 213 | +#!/bin/bash |
| 214 | +qemu-system-riscv64 -machine virt -nographic -bios default -kernel build/kernel.elf |
| 215 | +``` |
| 216 | + |
| 217 | +**File: `scripts/demo.sh`** |
| 218 | + |
| 219 | +```bash |
| 220 | +#!/bin/bash |
| 221 | +(sleep 1; echo "help"; sleep 1; echo "ps"; sleep 1; |
| 222 | + echo "run cpu"; sleep 1; echo "run io"; sleep 1; |
| 223 | + echo "sched rr"; sleep 1; echo "bench") | ./scripts/run-qemu.sh |
| 224 | +``` |
| 225 | + |
| 226 | +### 12. Documentation |
| 227 | + |
| 228 | +**File: `docs/README.md`** |
| 229 | + |
| 230 | +- Requirements: QEMU, riscv64-unknown-elf toolchain |
| 231 | +- Build steps: make, make run |
| 232 | +- Shell commands reference |
| 233 | +- RR vs SJF explanation with metrics definitions |
| 234 | +- Example output log |
| 235 | + |
| 236 | +## Definition of Done |
| 237 | + |
| 238 | +- `make run` boots to "uROS ready" banner |
| 239 | +- Interactive prompt "uROS> " accepts commands |
| 240 | +- `help` lists all commands |
| 241 | +- `run cpu` and `run io` create visible tasks |
| 242 | +- `ps` shows task states and metrics |
| 243 | +- `sched rr` and `sched sjf` switch modes |
| 244 | +- `bench` prints comparison table with wait, turnaround, throughput |
| 245 | +- `uptime` and `meminfo` work correctly |
| 246 | +- Tasks preempt visibly under RR |
| 247 | +- Context switch preserves registers correctly |
| 248 | + |
| 249 | +### To-dos |
| 250 | + |
| 251 | +- [ ] Create include/uros.h with types, structs (context_t, pcb_t), and all function prototypes |
| 252 | +- [ ] Implement linker.ld with BASE=0x80200000, sections, and stack |
| 253 | +- [ ] Implement boot/start.S with entry point and ctx_switch assembly routine |
| 254 | +- [ ] Implement drivers/uart.c for NS16550A at 0x10000000 |
| 255 | +- [ ] Implement lib/printf.c with kprintf supporting %s %d %u %x |
| 256 | +- [ ] Implement drivers/timer.c with SBI calls and 100Hz tick scheduling |
| 257 | +- [ ] Implement kernel/trap.c with stvec setup, SIE/STIE enable, and timer interrupt handler |
| 258 | +- [ ] Implement kernel/task.c with bump allocator, task_create, yield, exit |
| 259 | +- [ ] Implement kernel/sched.c with RR preemptive and SJF non-preemptive modes |
| 260 | +- [ ] Implement kernel/shell.c with all commands including bench with RR vs SJF comparison |
| 261 | +- [ ] Implement kernel/kmain.c with initialization sequence and banner |
| 262 | +- [ ] Create Makefile with all/run/clean/dtb targets |
| 263 | +- [ ] Create scripts/run-qemu.sh and scripts/demo.sh |
| 264 | +- [ ] Create docs/README.md with requirements, usage, and explanation |
0 commit comments