diff --git a/Makefile b/Makefile index ae9a4bc..e40700e 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ $(OUT_DIR)start.o: kernel/start.s # Pattern rule for any .c → build/*.o $(OUT_DIR)%.o: %.c @mkdir -p $(OUT_DIR) - $(CC) $(CFLAGS) -c $< -o $@ + $(CC) $(CFLAGS) -c $< -o $@ $(KFLAGS) # KFLAGS is used for custom kernel flags # Link everything $(OUT_DIR)kernel.elf: $(OUT_DIR)start.o $(OBJS) kernel.ld diff --git a/README.md b/README.md index efab734..9a8b9a7 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,11 @@ Make sure you have an ARM cross-compiler installed (e.g., `arm-none-eabi-gcc`) a make ``` +Developers also have the option to run the kernel with custom flags such as: +```sh +make KFLAGS="-USE_KTESTS -MY_DEFFLAG" +``` + > [!IMPORTANT] > > `make` will clean, build, and run the kernel in QEMU. You can also run @@ -64,4 +69,4 @@ For more details about this kernel, refer to the [AstraKernel Documentation](htt ## License -This project is licensed under the GNU GENERAL PUBLIC License. See the [LICENSE](LICENSE) file for details. \ No newline at end of file +This project is licensed under the GNU GENERAL PUBLIC License. See the [LICENSE](LICENSE) file for details. diff --git a/include/interrupt.h b/include/interrupt.h new file mode 100644 index 0000000..0c898be --- /dev/null +++ b/include/interrupt.h @@ -0,0 +1,56 @@ +#ifndef INTERRUPT_H +#define INTERRUPT_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +// PL190 VIC (interrupt controller) +// ref: PrimeCell Vectored Interrupt Controller (PL190) TRM (Page3-7) +#define VIC_BASE 0x10140000u +#define VIC_INTSELECT (*(volatile uint32_t *)(VIC_BASE + 0x00C)) // 0=IRQ,1=FIQ +#define VIC_INTENABLE (*(volatile uint32_t *)(VIC_BASE + 0x010)) // set bit=enable +#define VIC_INT_ENCLR (*(volatile utin32_t *)(VIC_BASE + 0x014)) +#define VIC_SOFT_INT (*(volatile uint32_t *)(VIC_BASE + 0x018)) +#define VIC_SOFT_INTCLR (*(volatile uint32_t *)(VIC_BASE + 0x01C)) + +// SP804 Timer0 in the 0/1 block +// ref: ARM Dual-Time Module (SP804) TRM (Page 3-2) +#define T01_BASE 0x101E2000u +#define T0_LOAD (*(volatile uint32_t *)(T01_BASE + 0x00)) +#define T0_VALUE (*(volatile uint32_t *)(T01_BASE + 0x04)) +#define T0_CONTROL (*(volatile uint32_t *)(T01_BASE + 0x08)) +#define T0_INTCLR (*(volatile uint32_t *)(T01_BASE + 0x0C)) +#define T0_MIS (*(volatile uint32_t *)(T01_BASE + 0x14)) + +// Bits for CONTROL (SP804) +// ref: ARM Dual-Time Module (SP804) TRM (Page 3-5) +#define TCTRL_ENABLE (1u << 7) // EN=bit7 +#define TCTRL_PERIODIC (1u << 6) // PERIODIC=bit6 +#define TCTRL_INTEN (1u << 5) // INTEN=bit5 +#define TCTRL_32BIT (1u << 1) // 32BIT=bit1 + +// VIC line number for Timer0/1 on Versatile +#define IRQ_TIMER01 4 + + static inline void timer0_start_periodic(uint32_t load) + { + T0_CONTROL = 0; // disable while reconfig + T0_LOAD = load; // ex: 10000 for ~100 Hz if TIMCLK≈1 MHz + T0_INTCLR = 1; // clear any pending interrupt + T0_CONTROL = TCTRL_32BIT | TCTRL_PERIODIC | TCTRL_INTEN | TCTRL_ENABLE; + } + + static inline void vic_enable_timer01_irq(void) + { + VIC_INTSELECT &= ~(1u << IRQ_TIMER01); // route to IRQ + VIC_INTENABLE |= (1u << IRQ_TIMER01); // enable line 4 + } +#ifdef __cplusplus +} +#endif + +#endif // INTERRUPT_H diff --git a/kernel/irq.c b/kernel/irq.c index 704840d..9ada276 100644 --- a/kernel/irq.c +++ b/kernel/irq.c @@ -1,11 +1,13 @@ #include #include "irq.h" +#include "interrupt.h" volatile unsigned int tick = 0; void irq_handler(void) { + T0_INTCLR = 1; // this ack/clear the device (SP804) tick++; } diff --git a/kernel/kernel.c b/kernel/kernel.c index f970299..eb6ad59 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -6,6 +6,7 @@ #include "clear.h" #include "string.h" #include "irq.h" +#include "interrupt.h" static const char *banner[] = { @@ -54,7 +55,30 @@ void irq_sanity_check(void) } } +// This function is for testing purposes. +// It test that the timer interrupt is firing as expected. +void not_main(void) +{ + puts("Time0 IRQ firing test!\r\n"); + + vic_enable_timer01_irq(); + timer0_start_periodic(10000); + irq_enable(); + + unsigned last = 0; + for (;;) + { + unsigned n = tick; + if (n != last && (n % 100) == 0) + { + puts("."); + last = n; + } + } +} + /* The following macros are for testing purposes. */ +#define TIMER_TICK_TEST not_main() #define SANITY_CHECK irq_sanity_check() #define CALL_SVC_0 __asm__ volatile ("svc #0") @@ -63,9 +87,12 @@ void kernel_main(void) { clear(); - /* TEST */ +/* TESTS */ +#ifdef USE_KTESTS SANITY_CHECK; CALL_SVC_0; + TIMER_TICK_TEST; +#endif /* Back to normal operations */ init_message(); diff --git a/literature/ARM_Compiler_User_Guide.pdf b/literature/ARM_Compiler_User_Guide.pdf new file mode 100644 index 0000000..22d13e3 Binary files /dev/null and b/literature/ARM_Compiler_User_Guide.pdf differ diff --git a/literature/ARM_Dual-Time Module_SP804.pdf b/literature/ARM_Dual-Time Module_SP804.pdf new file mode 100644 index 0000000..8ef997e Binary files /dev/null and b/literature/ARM_Dual-Time Module_SP804.pdf differ diff --git a/literature/PrimeCell_Vectored_Interrupt_Controller_PL190.pdf b/literature/PrimeCell_Vectored_Interrupt_Controller_PL190.pdf new file mode 100644 index 0000000..a368a35 Binary files /dev/null and b/literature/PrimeCell_Vectored_Interrupt_Controller_PL190.pdf differ