From 537dde170bebca8154b8c75ed7d739ca74fc9c52 Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Mon, 16 Feb 2026 17:41:07 +0000 Subject: [PATCH 1/7] (timer): basic start --- src/kernel/interrupts.c | 5 ++--- src/kernel/timer.c | 12 ++++++++++++ src/kernel/timer.h | 8 ++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 src/kernel/timer.c create mode 100644 src/kernel/timer.h diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index 6647a0f..ac14bb2 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -6,6 +6,7 @@ #include #include #include +#include void interrupt_init() { @@ -115,9 +116,7 @@ void handle_interrupt(unsigned long code) switch (code) { case 7: - static volatile uint64_t *mtime = (uint64_t *)CLINT_MTIME; - static volatile uint64_t *mtimecmp = (uint64_t *)CLINT_MTIMECMP(0); - *mtimecmp = *mtime + 100000; + timer_handle_interrupt(); break; case 11: diff --git a/src/kernel/timer.c b/src/kernel/timer.c new file mode 100644 index 0000000..530c6fd --- /dev/null +++ b/src/kernel/timer.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +void timer_handle_interrupt() +{ + static volatile uint64_t *mtime = (uint64_t *)CLINT_MTIME; + static volatile uint64_t *mtimecmp = (uint64_t *)CLINT_MTIMECMP(0); + + *mtimecmp = *mtime + TIMER_FREQ; +} \ No newline at end of file diff --git a/src/kernel/timer.h b/src/kernel/timer.h new file mode 100644 index 0000000..3b6f452 --- /dev/null +++ b/src/kernel/timer.h @@ -0,0 +1,8 @@ +#ifndef KTIMER_H +#define KTIMER_H + +#define TIMER_FREQ 100000 + +void timer_handle_interrupt(); + +#endif \ No newline at end of file From 0176b636c55121d3a342c77149954fc1699de7c1 Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Fri, 1 May 2026 21:05:12 +0100 Subject: [PATCH 2/7] Save floating point unit registers --- src/boot/traps.S | 111 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 99 insertions(+), 12 deletions(-) diff --git a/src/boot/traps.S b/src/boot/traps.S index 9c2f024..ae3047e 100644 --- a/src/boot/traps.S +++ b/src/boot/traps.S @@ -4,10 +4,10 @@ .global trap_entry trap_entry: - # Create space on the stack for 32 registers (32 * 8 = 256 bytes) - addi sp, sp, -256 + # Allocate stack space for GPRs (256 bytes) + FPU registers (512 bytes) + addi sp, sp, -768 # Total: 256 (GPRs) + 512 (FPU) - # Save all General Purpose Registers (GPRs) + # --- Save GPRs --- sd ra, 0(sp) sd gp, 8(sp) sd tp, 16(sp) @@ -39,19 +39,108 @@ trap_entry: sd t5, 224(sp) sd t6, 232(sp) - # Save mepc to the stack (using offset 240) + # --- Save mepc --- csrr t0, mepc sd t0, 240(sp) - # Call trap handler - mv a0, sp # put the stack pointer to the first arg + # --- Check FPU Status (mstatus.FS) --- + csrr t1, mstatus + li t2, 0x6000 + and t1, t1, t2 + bnez t1, save_fpu + + # If no FPU, skip to handler + j call_handler + +save_fpu: + # Save FPU registers (f0-f31) starting at offset 256 + fsd f0, 256(sp) + fsd f1, 264(sp) + fsd f2, 272(sp) + fsd f3, 280(sp) + fsd f4, 288(sp) + fsd f5, 296(sp) + fsd f6, 304(sp) + fsd f7, 312(sp) + fsd f8, 320(sp) + fsd f9, 328(sp) + fsd f10, 336(sp) + fsd f11, 344(sp) + fsd f12, 352(sp) + fsd f13, 360(sp) + fsd f14, 368(sp) + fsd f15, 376(sp) + fsd f16, 384(sp) + fsd f17, 392(sp) + fsd f18, 400(sp) + fsd f19, 408(sp) + fsd f20, 416(sp) + fsd f21, 424(sp) + fsd f22, 432(sp) + fsd f23, 440(sp) + fsd f24, 448(sp) + fsd f25, 456(sp) + fsd f26, 464(sp) + fsd f27, 472(sp) + fsd f28, 480(sp) + fsd f29, 488(sp) + fsd f30, 496(sp) + fsd f31, 504(sp) + +call_handler: + # Pass stack pointer to C handler + mv a0, sp call handle_trap - # restore mepc + # --- Restore mepc --- ld t0, 240(sp) csrw mepc, t0 - # Restore all GPRs + # --- Restore FPU (if it was saved) --- + csrr t1, mstatus + li t2, 0x6000 + and t1, t1, t2 + bnez t1, restore_fpu + + j restore_gpr + +restore_fpu: + # Restore FPU registers + fld f0, 256(sp) + fld f1, 264(sp) + fld f2, 272(sp) + fld f3, 280(sp) + fld f4, 288(sp) + fld f5, 296(sp) + fld f6, 304(sp) + fld f7, 312(sp) + fld f8, 320(sp) + fld f9, 328(sp) + fld f10, 336(sp) + fld f11, 344(sp) + fld f12, 352(sp) + fld f13, 360(sp) + fld f14, 368(sp) + fld f15, 376(sp) + fld f16, 384(sp) + fld f17, 392(sp) + fld f18, 400(sp) + fld f19, 408(sp) + fld f20, 416(sp) + fld f21, 424(sp) + fld f22, 432(sp) + fld f23, 440(sp) + fld f24, 448(sp) + fld f25, 456(sp) + fld f26, 464(sp) + fld f27, 472(sp) + fld f28, 480(sp) + fld f29, 488(sp) + fld f30, 496(sp) + fld f31, 504(sp) + +restore_gpr: + # Restore GPRs ld ra, 0(sp) ld gp, 8(sp) ld tp, 16(sp) @@ -83,8 +172,6 @@ trap_entry: ld t5, 224(sp) ld t6, 232(sp) - # Shrink the stack back - addi sp, sp, 256 - - # Return from Machine-mode trap + # Restore stack pointer + addi sp, sp, 768 mret \ No newline at end of file From f76ca1ee31f795be6ee013eec3e63beafed0f7bc Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Fri, 1 May 2026 22:34:27 +0100 Subject: [PATCH 3/7] traps/interrupts: Refactor trap handling and align CSR access --- src/boot/traps.S | 6 +++++- src/kernel/interrupts.c | 13 ++++--------- src/kernel/interrupts.h | 8 ++++++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/boot/traps.S b/src/boot/traps.S index ae3047e..ed84028 100644 --- a/src/boot/traps.S +++ b/src/boot/traps.S @@ -39,9 +39,13 @@ trap_entry: sd t5, 224(sp) sd t6, 232(sp) - # --- Save mepc --- + # --- Save mepc/mcause/mstatus --- csrr t0, mepc sd t0, 240(sp) + csrr t0, mcause + sd t0, 248(sp) + csrr t0, mstatus + sd t0, 256(sp) # --- Check FPU Status (mstatus.FS) --- csrr t1, mstatus diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index 6647a0f..479e7c3 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -54,15 +54,11 @@ void kpanic_force() void handle_trap(trap_frame_t *registers) { - // Read the 'mcause' register to see WHY we trapped - unsigned long cause; - __asm__ volatile("csrr %0, mcause" : "=r"(cause)); - // Check if the top bit is 1 (Interrupt) or 0 (Exception) // For 64-bit RISC-V, the bit is 63 - int is_interrupt = (cause >> 63) & 1; + int is_interrupt = (registers->mcause >> 63) & 1; - unsigned long code = cause & 0xfff; + unsigned long code = registers->mcause & 0xfff; if (is_interrupt) { @@ -77,7 +73,7 @@ void handle_trap(trap_frame_t *registers) kprintf("\n[EXCEPTION] Code: %d | Instruction: %x | Fault Address: %x\n", code, registers->mepc, mtval); - switch (cause) + switch (registers->mcause) { case 0: kpanic("Reason: Instruction Address Misaligned\n"); @@ -104,7 +100,7 @@ void handle_trap(trap_frame_t *registers) kpanic("Reason: Store/AMO Access Fault\n"); break; default: - kpanic("Reason: Unknown Exception Code %d\n", cause); + kpanic("Reason: Unknown Exception Code %d\n", registers->mcause); break; } } @@ -118,7 +114,6 @@ void handle_interrupt(unsigned long code) static volatile uint64_t *mtime = (uint64_t *)CLINT_MTIME; static volatile uint64_t *mtimecmp = (uint64_t *)CLINT_MTIMECMP(0); *mtimecmp = *mtime + 100000; - break; case 11: volatile uint32_t *claim_reg = (uint32_t *)PLIC_CLAIM(0); diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h index 0bd683d..8d108fb 100644 --- a/src/kernel/interrupts.h +++ b/src/kernel/interrupts.h @@ -3,6 +3,8 @@ #define MSTATUS 0x300 #define MIE 0x304 +#define MCAUSE 0x342 +#define MEPC 0x341 #define MSTATUS_BIT_MIE 3 #define MIE_BIT_MTIE 7 #define MIE_BIT_MEIE 11 @@ -56,8 +58,10 @@ typedef struct { uint64_t t5; // x30 uint64_t t6; // x31 - // Control and Status Register state - uint64_t mepc; // offset 240 (Saved in traps.S) + // Control and Status Register state (Saved in traps.S) + uint64_t mepc; // offset 240 + uint64_t mcause; // offset 248 + uint64_t mstatus; // offset 256 } trap_frame_t; void interrupt_init(); From ebdd572f8ee6493dd8e61d8d82c1d3ad5f10ac9f Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Fri, 1 May 2026 23:14:44 +0100 Subject: [PATCH 4/7] feat: implement basic timer --- src/kernel/interrupts.c | 6 +++--- src/kernel/kernel.c | 10 ++++++++++ src/kernel/plic.c | 8 +++++++- src/kernel/plic.h | 1 + src/kernel/timer.c | 39 +++++++++++++++++++++++++++++++++++++++ src/kernel/timer.h | 23 +++++++++++++++++++++++ 6 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 src/kernel/timer.c create mode 100644 src/kernel/timer.h diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index 479e7c3..366f1c3 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -6,6 +6,8 @@ #include #include #include +#include + void interrupt_init() { @@ -111,9 +113,7 @@ void handle_interrupt(unsigned long code) switch (code) { case 7: - static volatile uint64_t *mtime = (uint64_t *)CLINT_MTIME; - static volatile uint64_t *mtimecmp = (uint64_t *)CLINT_MTIMECMP(0); - *mtimecmp = *mtime + 100000; + timer_tick(); break; case 11: volatile uint32_t *claim_reg = (uint32_t *)PLIC_CLAIM(0); diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 1d935c3..da2a669 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -5,11 +5,13 @@ #include #include #include +#include void kmain() { zero_bss(); plic_init(); + timer_init(); uart_init(); page_init(); interrupt_init(); @@ -17,4 +19,12 @@ void kmain() kprintf("Hello, from %s!", "SquidgeOS"); kputs("----------------------"); knewline(); + while (1) { + uint64_t ticks = timer_get_ms(); + kputchar('T'); + kputchar(':'); // Print "T:1234" every second + kprint_int(ticks); + knewline(); + delay_ms(1000); + } } diff --git a/src/kernel/plic.c b/src/kernel/plic.c index 5a14838..86e6792 100644 --- a/src/kernel/plic.c +++ b/src/kernel/plic.c @@ -13,7 +13,13 @@ void plic_init() // This is a bitmask, so we shift 1 by the IRQ number. *PLIC_ENABLE(hart) = (1 << UART_IRQ); + // Set priority for CLINT timer interrupt (IRQ 5) + *PLIC_PRIORITY(TIMER_IRQ) = 1; // Lowest priority + + // Enable CLINT timer interrupt for Hart 0 + *PLIC_ENABLE(hart) |= (1 << TIMER_IRQ); + // 3. Set the priority threshold for Hart 0 // We set this to 0 so that ANY interrupt with priority > 0 gets through. *PLIC_THRESHOLD(hart) = 0; -} \ No newline at end of file +} diff --git a/src/kernel/plic.h b/src/kernel/plic.h index 9235164..3e72087 100644 --- a/src/kernel/plic.h +++ b/src/kernel/plic.h @@ -16,6 +16,7 @@ #define PLIC_CLAIM(hart) ((volatile uint32_t *)(PLIC_BASE + 0x200004 + (hart) * 0x1000)) #define UART_IRQ 10 +#define TIMER_IRQ 5 void plic_init(); diff --git a/src/kernel/timer.c b/src/kernel/timer.c new file mode 100644 index 0000000..2267aef --- /dev/null +++ b/src/kernel/timer.c @@ -0,0 +1,39 @@ +#include +#include "timer.h" +#include "plic.h" +#include "interrupts.h" + +_Atomic uint64_t sys_ticks = 0; +const uint64_t tick_delta = 1000; + +void timer_init() { + timer_tick(); +} + +void timer_tick() { + // Set the first timer interrupt to fire after 1ms + static volatile uint64_t *mtime = (uint64_t *)CLINT_MTIME; + static volatile uint64_t *mtimecmp = (uint64_t *)CLINT_MTIMECMP(0); + *mtimecmp = *mtime + tick_delta; + sys_ticks++; +} + +// Get current time in milliseconds +uint64_t timer_get_ms() { + return sys_ticks; +} + +// Delay for 'ms' milliseconds (blocking) +void delay_ms(uint64_t ms) { + if (ms == 0) return; + uint64_t end = sys_ticks + ms; + while (sys_ticks < end) { + asm volatile("wfi"); + } +} + +// Sleep for 'ms' milliseconds (non-blocking, if possible) +// For now, just call delay_ms (can be improved later) +void sleep_ms(uint64_t ms) { + delay_ms(ms); +} \ No newline at end of file diff --git a/src/kernel/timer.h b/src/kernel/timer.h new file mode 100644 index 0000000..ac0a2af --- /dev/null +++ b/src/kernel/timer.h @@ -0,0 +1,23 @@ +#ifndef TIMER_H +#define TIMER_H +#include + +extern _Atomic uint64_t sys_ticks; +extern const uint64_t tick_delta; + +// Initialize timer (1ms ticks) +void timer_init(); + +// Increment the system tick counter +void timer_tick(); + +// Get current time in milliseconds +uint64_t timer_get_ms(); + +// Delay for 'ms' milliseconds (blocking) +void delay_ms(uint64_t ms); + +// Sleep for 'ms' milliseconds (non-blocking, if possible) +void sleep_ms(uint64_t ms); + +#endif \ No newline at end of file From 5f09049765ea486bfd4038e401ba6132e89fe2da Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Sat, 2 May 2026 01:53:03 +0100 Subject: [PATCH 5/7] Fix RISC-V timer interrupt (IRQ 7) and enable proper interrupt handling - Correct IRQ number for CLINT timer from 5 to 7 in PLIC and headers. - Add debug prints for MIE, SIE, and MSTATUS CSRs to verify interrupt enablement. - Enable timer (IRQ 7) and global interrupts in MIE/SIE registers. - Update `timer_tick()` to log `mtimecmp` and `mtime` for debugging. - Ensure `stvec` is set and interrupts are properly acknowledged. --- src/kernel/interrupts.c | 11 +++++++++++ src/kernel/plic.c | 2 +- src/kernel/plic.h | 2 +- src/kernel/timer.c | 8 ++++++-- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index 366f1c3..c700b19 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -23,6 +23,17 @@ void interrupt_init() mie_val |= (1 << MIE_BIT_MEIE) | (1 << MIE_BIT_MTIE); asm volatile("csrw mie, %0" ::"r"(mie_val)); kputs("OK"); + + uint32_t mie, sie, mstatus; + asm volatile ("csrr %0, mie" : "=r"(mie)); + asm volatile ("csrr %0, sie" : "=r"(sie)); + asm volatile ("csrr %0, mstatus" : "=r"(mstatus)); + kprintf("mie: 0x%X, sie: 0x%X, mstatus: 0x%X\n", mie, sie, mstatus); + + // Enable timer and global interrupts + asm volatile ("csrs mie, %0" : : "r"(mie | (1 << 7) | (1 << 3))); // Enable timer and global + asm volatile ("csrs sie, %0" : : "r"(sie | (1 << 7) | (1 << 3))); // Enable timer and global + asm volatile ("csrs mstatus, %0" : : "r"(mstatus | (1 << 3))); // Set MIE bit } void kpanic(const char *reason, ...) diff --git a/src/kernel/plic.c b/src/kernel/plic.c index 86e6792..f482fa8 100644 --- a/src/kernel/plic.c +++ b/src/kernel/plic.c @@ -13,7 +13,7 @@ void plic_init() // This is a bitmask, so we shift 1 by the IRQ number. *PLIC_ENABLE(hart) = (1 << UART_IRQ); - // Set priority for CLINT timer interrupt (IRQ 5) + // Set priority for CLINT timer interrupt (IRQ 7) *PLIC_PRIORITY(TIMER_IRQ) = 1; // Lowest priority // Enable CLINT timer interrupt for Hart 0 diff --git a/src/kernel/plic.h b/src/kernel/plic.h index 3e72087..cca2c60 100644 --- a/src/kernel/plic.h +++ b/src/kernel/plic.h @@ -16,7 +16,7 @@ #define PLIC_CLAIM(hart) ((volatile uint32_t *)(PLIC_BASE + 0x200004 + (hart) * 0x1000)) #define UART_IRQ 10 -#define TIMER_IRQ 5 +#define TIMER_IRQ 7 void plic_init(); diff --git a/src/kernel/timer.c b/src/kernel/timer.c index 2267aef..fe57e23 100644 --- a/src/kernel/timer.c +++ b/src/kernel/timer.c @@ -1,7 +1,9 @@ #include +#include #include "timer.h" #include "plic.h" #include "interrupts.h" +#include "drivers/uart.h" _Atomic uint64_t sys_ticks = 0; const uint64_t tick_delta = 1000; @@ -11,10 +13,12 @@ void timer_init() { } void timer_tick() { - // Set the first timer interrupt to fire after 1ms static volatile uint64_t *mtime = (uint64_t *)CLINT_MTIME; static volatile uint64_t *mtimecmp = (uint64_t *)CLINT_MTIMECMP(0); - *mtimecmp = *mtime + tick_delta; + uint64_t current_mtime = *mtime; + uint64_t new_mtimecmp = current_mtime + tick_delta; + *mtimecmp = new_mtimecmp; + //kprintf("Set mtimecmp: %u (mtime: %u, delta: %u)\n", new_mtimecmp, current_mtime, tick_delta); sys_ticks++; } From 40333f1a37a5a1fd5c606d8e30fac7c90f040cfb Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Sat, 2 May 2026 03:19:15 +0100 Subject: [PATCH 6/7] timer: Refactor tick_delta to use hardware-specific clock frequency --- src/kernel/timer.c | 2 +- src/kernel/timer.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/kernel/timer.c b/src/kernel/timer.c index fe57e23..03320da 100644 --- a/src/kernel/timer.c +++ b/src/kernel/timer.c @@ -6,7 +6,7 @@ #include "drivers/uart.h" _Atomic uint64_t sys_ticks = 0; -const uint64_t tick_delta = 1000; +const uint64_t tick_delta = TICKS_PER_MS; void timer_init() { timer_tick(); diff --git a/src/kernel/timer.h b/src/kernel/timer.h index ac0a2af..089010f 100644 --- a/src/kernel/timer.h +++ b/src/kernel/timer.h @@ -2,6 +2,13 @@ #define TIMER_H #include +/* Define your clock frequency here based on your hardware */ +/* Example: 12.5 MHz for QEMU virt machine */ +#define CLOCK_FREQ_HZ 12500000 + +/* Calculate ticks per millisecond */ +#define TICKS_PER_MS (CLOCK_FREQ_HZ / 1000) + extern _Atomic uint64_t sys_ticks; extern const uint64_t tick_delta; From 57a21bd4e2dd7317537f6a94bcd94bcab9b9a05f Mon Sep 17 00:00:00 2001 From: Liam Kerr Date: Sat, 2 May 2026 04:15:37 +0100 Subject: [PATCH 7/7] style: format kprintf_internal for improved readability Co-authored-by: Copilot --- src/drivers/uart.c | 22 ++++++++++++---------- src/lib/string.h | 3 +++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/drivers/uart.c b/src/drivers/uart.c index a2c216a..df5d4fd 100644 --- a/src/drivers/uart.c +++ b/src/drivers/uart.c @@ -137,7 +137,8 @@ void kprintf_internal(const char *format, va_list args) if (*p == '%') { p++; - if(*p == '\0') break; + if (*p == '\0') + break; switch (*p) { case 'c': @@ -149,7 +150,8 @@ void kprintf_internal(const char *format, va_list args) case 's': { char *s = va_arg(args, char *); - if (!s) s = "(null)"; + if (!s) + s = "(null)"; kprint(s); break; } @@ -159,12 +161,12 @@ void kprintf_internal(const char *format, va_list args) kprint_int(d); break; } - case 'u': - { + case 'u': + { unsigned int u = va_arg(args, unsigned int); kprint_int((int)u); break; - } + } case 'f': { double f = va_arg(args, double); @@ -174,11 +176,11 @@ void kprintf_internal(const char *format, va_list args) case 'x': // Hex case 'p': // Pointer { - #if UINTPTR_MAX == 0xffffffffULL - unsigned int x = va_arg(args, unsigned int); - #else - uint64_t x = va_arg(args, uint64_t); - #endif +#if UINTPTR_MAX == 0xffffffffULL + unsigned int x = va_arg(args, unsigned int); +#else + uint64_t x = va_arg(args, uint64_t); +#endif kprint_hex(x); break; } diff --git a/src/lib/string.h b/src/lib/string.h index 78ae169..5ad4eee 100644 --- a/src/lib/string.h +++ b/src/lib/string.h @@ -2,5 +2,8 @@ #define STRING_H void *memset(void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +int strcmp(const char *str1, const char *str2); +size_t strlen(const char *s); #endif \ No newline at end of file