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.
Этот коммит содержится в:
Ayke van Laethem 2020-01-09 16:38:17 +01:00 коммит произвёл Ron Evans
родитель 360923abbf
коммит b9cdfd9e9a
3 изменённых файлов: 102 добавлений и 3 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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")
}
}

Просмотреть файл

@ -2,7 +2,10 @@ SECTIONS
{
.text :
{
. = ALIGN(4);
KEEP(*(.init))
. = ALIGN(4);
*(.text.handleInterruptASM)
*(.text)
*(.text.*)
*(.rodata)