From 06f000e19aa32d41b8692ef82f9ca87049fec3b3 Mon Sep 17 00:00:00 2001 From: giomba Date: Sun, 6 Jan 2019 17:33:02 +0100 Subject: [PATCH] Interrupts and Timers --- include/gic.h | 45 +++++++++++++++++++++++++++++++++ include/timer.h | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ src/dbg.cpp | 4 +-- src/dbg.s | 17 ------------- src/gic.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++ src/kernel.cpp | 11 ++++++++- src/linker.ld | 5 ++++ src/startup.s | 16 ++++++++++++ src/timer.cpp | 39 +++++++++++++++++++++++++++++ src/vectab.cpp | 32 ++++++++++++++++++++++++ src/vectab.s | 31 ++++++++++++++++++----- 11 files changed, 296 insertions(+), 27 deletions(-) create mode 100644 include/gic.h create mode 100644 include/timer.h create mode 100644 src/gic.cpp create mode 100644 src/timer.cpp create mode 100644 src/vectab.cpp diff --git a/include/gic.h b/include/gic.h new file mode 100644 index 0000000..4296625 --- /dev/null +++ b/include/gic.h @@ -0,0 +1,45 @@ +#ifndef GIC_H +#define GIC_H + +#include + +namespace GIC { + + /* GIC Distributor and CPU Interface base addresses */ + uint32_t* const distributor = (uint32_t*)(0x01c81000); + uint32_t* const cpuInterface = (uint32_t*)(0x01c82000); + + /* GIC Distributor Registers Offsets */ + const uint16_t GICD_CTLR = 0x000 / 4; + const uint16_t GICD_ISENABLER = 0x100 / 4; + const uint16_t GICD_ICENABLER = 0x180 / 4; + const uint16_t GICD_ITARGETSR = 0x800 / 4; + const uint16_t GICD_ICFGR = 0xc00 / 4; + + /* GIC CPU Interface Registers Offsets */ + const uint16_t GICC_CTLR = 0x0000 / 4; + const uint16_t GICC_PMR = 0x0004 / 4; + const uint16_t GICC_IAR = 0x000c / 4; + const uint16_t GICC_EOIR = 0x0010 / 4; + + enum Sensitivity { + LEVEL = 0, + EDGE = 1 + }; + enum TargetCPU { + CPU0T = 1, + CPU1T = 2, + CPU2T = 4, + CPU3T = 8, + CPU4T = 16, + CPU5T = 32, + CPU6T = 64, + CPU7T = 128, + CPUALLT = 255 + }; + + void enable(void); +} + +#endif + diff --git a/include/timer.h b/include/timer.h new file mode 100644 index 0000000..d7b55d0 --- /dev/null +++ b/include/timer.h @@ -0,0 +1,66 @@ +#ifndef TIMER_H +#define TIMER_H + +#include + +namespace Timer { + /* Timer peripheral (with common registers) */ + uint32_t* const TMR_PERIPHERAL_BASE = (uint32_t*)(0x01c20c00); + struct Peripheral { + uint32_t TMR_IRQ_EN_REG; + uint32_t TMR_IRQ_STA_REG; + }; + + /* Timer modules array */ + uint32_t* const TMR_MODULE_BASE = (uint32_t*)(0x01c20c10); + struct TimerModule { + uint32_t TMR_CTLR_REG; + uint32_t TMR_INTV_VALUE_REG; + uint32_t TMR_CUR_VALUE_REG; + }; + enum Mode { + CONTINUOUS = 0, + SINGLE = 1 + }; + enum Prescaler { /* maybe PRESCALERx = log2(x) */ + PRESCALER_1 = 0, + PRESCALER_2 = 1, + PRESCALER_4 = 2, + PRESCALER_8 = 3, + PRESCALER_16 = 4, + PRESCALER_32 = 5, + PRESCALER_64 = 6, + PRESCALER_128 = 7 + + }; + enum ClockSource { + LS_OSC = 0, /* Low speed OSC */ + OSC24M = 1, /* OSC24M */ + PLL66 = 2 /* PLL6/6 */ + }; + + Peripheral* const peripheral = (Peripheral* const)(TMR_PERIPHERAL_BASE); + + class Timer { + private: + uint8_t n; + TimerModule* module; + public: + /* Initialize class instance as timer n + * where n is found on Allwinner datasheet */ + Timer(uint8_t n); + /* Prepare timer */ + void set(uint32_t interval, Mode mode, Prescaler prescaler, ClockSource source); + /* Reload and start timer */ + void start(void); + /* Stop timer */ + void stop(void); + }; + + /* Vector of system's timers */ + extern Timer timer[]; + +} + +#endif + diff --git a/src/dbg.cpp b/src/dbg.cpp index 34a6ca9..9e51d88 100644 --- a/src/dbg.cpp +++ b/src/dbg.cpp @@ -4,9 +4,7 @@ /* WARNING: use of static variable is not thread safe!! */ static char string[32]; -/* Never call this function directly: always use the printk, - * which disables and reenables interrupts */ -extern "C" int c_printk(const char* msg) { +extern "C" int printk(const char* msg) { while (*msg != '\0') { while ((*dbg::UART_LSR & 0x20) == 0); /* Wait for transmission */ *dbg::UART_THR = *msg; diff --git a/src/dbg.s b/src/dbg.s index 28e72ea..bb944f6 100644 --- a/src/dbg.s +++ b/src/dbg.s @@ -3,23 +3,6 @@ panic_msg: .string "=== PANIC : I'm afraid I have lost my towel" .text - -.extern c_printk -.global printk -printk: - push {lr} - - /* disables IRQ and FIQ in CPU */ - cpsid if - - /* C-Implementation */ - bl c_printk - - /* enables IRQ and FIQ in CPU */ - cpsie if - - pop {pc} - /* TODO: to be implemented in a more useful way */ .global panic panic: diff --git a/src/gic.cpp b/src/gic.cpp new file mode 100644 index 0000000..b7b976d --- /dev/null +++ b/src/gic.cpp @@ -0,0 +1,57 @@ +#include +#include + +namespace GIC { + + /* This code (correctly) assumes that initial registers' values are 0x0, + * thus does not work (read: correctly overwrite) with subsequent changes, + * but since GIC is prepared at system startup and then never touched again, + * this should not be a problem */ + void enableInterrupt(const uint16_t m, const TargetCPU target, const Sensitivity sensitivity) { + /* register offset (used in an uint32_t array, so no need to multiply by 4) */ + uint16_t n; + + /* Enable interrupt m */ + n = m / 32; + distributor[GICD_ISENABLER + n] = distributor[GICD_ISENABLER + n] | (1 << (m % 32)); + + /* Set target CPU */ + n = m / 4; + distributor[GICD_ITARGETSR + n] = distributor[GICD_ITARGETSR + n] | (target << ((m % 4) * 8)); + + /* Edge or level triggered? */ + n = m / 16; + distributor[GICD_ICFGR + n] = distributor[GICD_ICFGR + n] | (sensitivity << (2 * (m % 16) + 1)); + + /* Enable all priorities */ + cpuInterface[1] = 0xff; + } + + void printDistributorStatus(void) { + for (short i = 0; i < 1024; ++i) { + printk("Distributor "); printk(itoa(i * 4)); printk(" : "); + printkl(itoa(distributor[i])); + } + } + void printCPUInterfaceStatus(void) { + for (short i = 0; i < 64; ++i) { + printk("CPUInterface "); printk(itoa(i * 4)); printk(" : "); + printkl(itoa(cpuInterface[i])); + } + } + + void enable(void) { + + enableInterrupt(54, CPU0T, EDGE); /* Timer0 */ + + /* Enable distributor and CPU interfaces + * Do this always at the end, + * since writing on GIC registers is not allowed when it is enabled */ + distributor[0] = distributor[GICD_CTLR] | 0x1; + cpuInterface[0] = cpuInterface[GICC_CTLR] | 0x1; + + } + +} + + diff --git a/src/kernel.cpp b/src/kernel.cpp index b753813..a8b7098 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -1,6 +1,8 @@ #include #include +#include #include +#include extern "C" void go_usr(void); @@ -13,7 +15,14 @@ extern "C" int main(int argc, char** argv) { printkl("Now firing test software interrupt..."); fireswi(); - printkl("Go user..."); + printkl("Now enabling GIC interrupts..."); + GIC::enable(); + + printkl("Now enabling timer..."); + Timer::timer[0].set(0x80, Timer::CONTINUOUS, Timer::PRESCALER_128, Timer::LS_OSC); + Timer::timer[0].start(); + + printkl("Now going user..."); go_usr(); printkl("EOK -- End of Kernel -- This point can not be reached"); diff --git a/src/linker.ld b/src/linker.ld index e41f76e..6ed2b8b 100644 --- a/src/linker.ld +++ b/src/linker.ld @@ -24,6 +24,11 @@ SECTIONS .text : ALIGN(0x4) { *(.text) } > m_ram + .init_array : ALIGN(0x4) { + _init_array_start = .; + *(.init_array) + _init_array_end = .; + } } diff --git a/src/startup.s b/src/startup.s index 0b8e00c..c4b7fac 100644 --- a/src/startup.s +++ b/src/startup.s @@ -27,6 +27,22 @@ _start: /* Enter SuperVisor Mode */ cps #0x13 + /* Call constructors */ + ldr r10, =_init_array_start + ldr r11, =_init_array_end +1: + cmp r10, r11 // if every constructor has been called + beq 3f + + // "resta di stucco รจ un barbatrucco" + ldr r0, [r10] // load constructor address in r0 + ldr lr, =2f // store return address in lr + mov pc, r0 // jump to address in r0 +2: + add r10, #4 // next constructor + b 1b +3: + /* Enable Vector Table BAR remapping (clears bit 13 of SCTLR) */ mrc p15, 0, r0, c1, c0, 0 and r0, #0xffffdfff diff --git a/src/timer.cpp b/src/timer.cpp new file mode 100644 index 0000000..691032b --- /dev/null +++ b/src/timer.cpp @@ -0,0 +1,39 @@ +#include +#include + +namespace Timer { + + Timer timer[3] = { Timer(0), Timer(1), Timer(2) }; + + Timer::Timer(uint8_t n) { + this->n = n; + module = (TimerModule*)(TMR_MODULE_BASE + n); + } + + void Timer::set(uint32_t interval, Mode mode, Prescaler prescaler, ClockSource source) { + /* zero fill control registers */ + module->TMR_CTLR_REG = 0; + module->TMR_CUR_VALUE_REG = 0; + + /* Set controls: mode, prescaler and source */ + module->TMR_CTLR_REG = (mode << 7) | (prescaler << 4) | (source << 2); + + /* Set timer value */ + module->TMR_INTV_VALUE_REG = interval; + + /* Enable interrupt */ + peripheral->TMR_IRQ_EN_REG = peripheral->TMR_IRQ_EN_REG | (1 << n); + + } + + void Timer::start(void) { + /* Reload interval value and start */ + module->TMR_CTLR_REG = module->TMR_CTLR_REG | 0x3; + } + + void Timer::stop(void) { + /* Clear stop/pause bit */ + module->TMR_CTLR_REG = module->TMR_CTLR_REG & 0xfffffffe; + } + +} diff --git a/src/vectab.cpp b/src/vectab.cpp new file mode 100644 index 0000000..905ff89 --- /dev/null +++ b/src/vectab.cpp @@ -0,0 +1,32 @@ +#include +#include + +extern "C" uint32_t c_irq_handler(uint32_t iar) { + uint8_t cpuID = (iar >> 10) & 0x7; + uint16_t interruptID = iar & 0x3ff; + + if (cpuID == 0) { + /* for + * SPI - Shared Peripheral Interrupt + * PPI - Private Peripheral Interrupt + */ + switch (interruptID) { + case 54: /* timer 0 */ /* TODO: improve this ugliness */ + uint32_t* const TMR_IRQ_STA_REG = (uint32_t* const)(0x01c20c04); + *TMR_IRQ_STA_REG = *TMR_IRQ_STA_REG | 0x1; + printk("Serving IRQ #"); + printkl(itoa(interruptID)); + break; + } + } + else { + /* for + * SGI - Software Generated Interrupts + */ + + /* to be done -- multiprocessor systems */ + } + + return iar; +} + diff --git a/src/vectab.s b/src/vectab.s index 91daec4..fa49673 100644 --- a/src/vectab.s +++ b/src/vectab.s @@ -9,7 +9,7 @@ swi_msg: prefetch_abt_msg: .string "Prefetch Abort" data_abt_msg: -.string "Data Abort" +.string "Data Abort @ " irq_msg: .string "IRQ" fiq_msg: @@ -69,20 +69,39 @@ data_abt_handler: stmfd sp!, {lr} ldr r0, =data_abt_msg + bl printk + ldmfd sp!, {r0} + bl itoa bl printkl b panic ldmfd sp!, {pc}^ -// TODO: untested irq_handler: - stmfd sp!, {lr} + // mask FIQ (IRQ already masked by hardware when entering IRQ mode) + cpsid f - ldr r0, =irq_msg - bl printkl + // Prepare return address and store context on stack + sub lr, #0x4 + stmfd sp!, {r0-r12, lr} - ldmfd sp!, {pc}^ + // Acknoweledge interrupt + ldr r10, =0x01c8200c // IAR + ldr r0, [r10] + push {r0} + + // C complex handler + bl c_irq_handler + + // Send End of Interrupt + pop {r0} + ldr r10, =0x01c82010 // EOIR + str r0, [r10] + + // Reload context from stack + // and also restore CPSR from SPSR (that caret) + ldmfd sp!, {r0-r12, pc}^ // TODO: untested fiq_handler: