From b9cdfd9e9a1384967e652477880e7ac58a55116a Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 9 Jan 2020 16:38:17 +0100 Subject: [PATCH] riscv: add bare-bones interrupt support This commit adds support for timer interrupts, replacing the busy loop that was used before. It is perhaps the most simple interrupt to implement and should serve as the basis for further interrupt support in RISC-V. --- src/device/riscv/start.S | 48 ++++++++++++++++++++++++++++++++ src/runtime/runtime_fe310.go | 54 ++++++++++++++++++++++++++++++++++-- targets/riscv.ld | 3 ++ 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/src/device/riscv/start.S b/src/device/riscv/start.S index dd6ce327..06c9c4a2 100644 --- a/src/device/riscv/start.S +++ b/src/device/riscv/start.S @@ -5,8 +5,56 @@ _start: // Load the stack pointer. la sp, _stack_top + // Load the globals pointer. The program will load pointers relative to this // register, so it must be set to the right value on startup. // See: https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register la gp, __global_pointer$ + + // Jump to runtime.main call main + +.section .text.handleInterruptASM +.global handleInterruptASM +.type handleInterruptASM,@function +handleInterruptASM: + // Save and restore all registers, because the hardware only saves/restores + // the pc. + // Note: we have to do this in assembly because the "interrupt"="machine" + // attribute is broken in LLVM: https://bugs.llvm.org/show_bug.cgi?id=42984 + addi sp, sp, -64 + sw ra, 60(sp) + sw t0, 56(sp) + sw t1, 52(sp) + sw t2, 48(sp) + sw a0, 44(sp) + sw a1, 40(sp) + sw a2, 36(sp) + sw a3, 32(sp) + sw a4, 28(sp) + sw a5, 24(sp) + sw a6, 20(sp) + sw a7, 16(sp) + sw t3, 12(sp) + sw t4, 8(sp) + sw t5, 4(sp) + sw t6, 0(sp) + call handleInterrupt + lw t6, 0(sp) + lw t5, 4(sp) + lw t4, 8(sp) + lw t3, 12(sp) + lw a7, 16(sp) + lw a6, 20(sp) + lw a5, 24(sp) + lw a4, 28(sp) + lw a3, 32(sp) + lw a2, 36(sp) + lw a1, 40(sp) + lw a0, 44(sp) + lw t2, 48(sp) + lw t1, 52(sp) + lw t0, 56(sp) + lw ra, 60(sp) + addi sp, sp, 64 + mret diff --git a/src/runtime/runtime_fe310.go b/src/runtime/runtime_fe310.go index b7a3774b..c3a10bf3 100644 --- a/src/runtime/runtime_fe310.go +++ b/src/runtime/runtime_fe310.go @@ -9,7 +9,9 @@ import ( "machine" "unsafe" + "device/riscv" "device/sifive" + "runtime/volatile" ) type timeUnit int64 @@ -31,12 +33,47 @@ var _edata unsafe.Pointer //go:export main func main() { + // Zero the PLIC enable bits on startup: they are not zeroed at reset. + sifive.PLIC.ENABLE[0].Set(0) + sifive.PLIC.ENABLE[1].Set(0) + + // Set the interrupt address. + // Note that this address must be aligned specially, otherwise the MODE bits + // of MTVEC won't be zero. + riscv.MTVEC.Set(uintptr(unsafe.Pointer(&handleInterruptASM))) + + // Enable global interrupts now that they've been set up. + riscv.MSTATUS.SetBits(1 << 3) // MIE + preinit() initAll() callMain() abort() } +//go:extern handleInterruptASM +var handleInterruptASM [0]uintptr + +//export handleInterrupt +func handleInterrupt() { + cause := riscv.MCAUSE.Get() + code := uint(cause &^ (1 << 31)) + if cause&(1<<31) != 0 { + // Topmost bit is set, which means that it is an interrupt. + switch code { + case 7: // Machine timer interrupt + // Signal timeout. + timerWakeup.Set(1) + // Disable the timer, to avoid triggering the interrupt right after + // this interrupt returns. + riscv.MIE.ClearBits(1 << 7) // MTIE bit + } + } else { + // TODO: handle exceptions in a similar was as HardFault is handled on + // Cortex-M (by printing an error message with instruction address). + } +} + func init() { pric_init() machine.UART0.Configure(machine.UARTConfig{}) @@ -81,6 +118,8 @@ func putchar(c byte) { const asyncScheduler = false +var timerWakeup volatile.Register8 + func ticks() timeUnit { // Combining the low bits and the high bits yields a time span of over 270 // years without counter rollover. @@ -99,7 +138,16 @@ func ticks() timeUnit { } func sleepTicks(d timeUnit) { - target := ticks() + d - for ticks() < target { + target := uint64(ticks() + d) + sifive.CLINT.MTIMECMPH.Set(uint32(target >> 32)) + sifive.CLINT.MTIMECMP.Set(uint32(target)) + riscv.MIE.SetBits(1 << 7) // MTIE + for { + if timerWakeup.Get() != 0 { + timerWakeup.Set(0) + // Disable timer. + break + } + riscv.Asm("wfi") } -} \ No newline at end of file +} diff --git a/targets/riscv.ld b/targets/riscv.ld index 86bcfaf0..7f4427a8 100644 --- a/targets/riscv.ld +++ b/targets/riscv.ld @@ -2,7 +2,10 @@ SECTIONS { .text : { + . = ALIGN(4); KEEP(*(.init)) + . = ALIGN(4); + *(.text.handleInterruptASM) *(.text) *(.text.*) *(.rodata)