mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 10:37:46 +00:00
transform: refactor interrupt lowering
Instead of doing everything in the interrupt lowering pass, generate
some more code in gen-device to declare interrupt handler functions and
do some work in the compiler so that interrupt lowering becomes a lot
simpler.
This has several benefits:
- Overall code is smaller, in particular the interrupt lowering pass.
- The code should be a bit less "magical" and instead a bit easier to
read. In particular, instead of having a magic
runtime.callInterruptHandler (that is fully written by the interrupt
lowering pass), the runtime calls a generated function like
device/sifive.InterruptHandler where this switch already exists in
code.
- Debug information is improved. This can be helpful during actual
debugging but is also useful for other uses of DWARF debug
information.
For an example on debug information improvement, this is what a
backtrace might look like before this commit:
Breakpoint 1, 0x00000b46 in UART0_IRQHandler ()
(gdb) bt
#0 0x00000b46 in UART0_IRQHandler ()
#1 <signal handler called>
[..etc]
Notice that the debugger doesn't see the source code location where it
has stopped.
After this commit, breaking at the same line might look like this:
Breakpoint 1, (*machine.UART).handleInterrupt (arg1=..., uart=<optimized out>) at /home/ayke/src/github.com/tinygo-org/tinygo/src/machine/machine_nrf.go:200
200 uart.Receive(byte(nrf.UART0.RXD.Get()))
(gdb) bt
#0 (*machine.UART).handleInterrupt (arg1=..., uart=<optimized out>) at /home/ayke/src/github.com/tinygo-org/tinygo/src/machine/machine_nrf.go:200
#1 UART0_IRQHandler () at /home/ayke/src/github.com/tinygo-org/tinygo/src/device/nrf/nrf51.go:176
#2 <signal handler called>
[..etc]
By now, the debugger sees an actual source location for UART0_IRQHandler
(in the generated file) and an inlined function.
This commit is contained in:
committed by
Ron Evans
parent
30bbdd5aeb
commit
edcece33ca
@@ -4,6 +4,7 @@ package machine
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -11,20 +12,20 @@ import (
|
||||
// Interrupt numbers as used on the GameBoy Advance. Register them with
|
||||
// runtime/interrupt.New.
|
||||
const (
|
||||
IRQ_VBLANK = 0
|
||||
IRQ_HBLANK = 1
|
||||
IRQ_VCOUNT = 2
|
||||
IRQ_TIMER0 = 3
|
||||
IRQ_TIMER1 = 4
|
||||
IRQ_TIMER2 = 5
|
||||
IRQ_TIMER3 = 6
|
||||
IRQ_COM = 7
|
||||
IRQ_DMA0 = 8
|
||||
IRQ_DMA1 = 9
|
||||
IRQ_DMA2 = 10
|
||||
IRQ_DMA3 = 11
|
||||
IRQ_KEYPAD = 12
|
||||
IRQ_GAMEPAK = 13
|
||||
IRQ_VBLANK = interrupt.IRQ_VBLANK
|
||||
IRQ_HBLANK = interrupt.IRQ_HBLANK
|
||||
IRQ_VCOUNT = interrupt.IRQ_VCOUNT
|
||||
IRQ_TIMER0 = interrupt.IRQ_TIMER0
|
||||
IRQ_TIMER1 = interrupt.IRQ_TIMER1
|
||||
IRQ_TIMER2 = interrupt.IRQ_TIMER2
|
||||
IRQ_TIMER3 = interrupt.IRQ_TIMER3
|
||||
IRQ_COM = interrupt.IRQ_COM
|
||||
IRQ_DMA0 = interrupt.IRQ_DMA0
|
||||
IRQ_DMA1 = interrupt.IRQ_DMA1
|
||||
IRQ_DMA2 = interrupt.IRQ_DMA2
|
||||
IRQ_DMA3 = interrupt.IRQ_DMA3
|
||||
IRQ_KEYPAD = interrupt.IRQ_KEYPAD
|
||||
IRQ_GAMEPAK = interrupt.IRQ_GAMEPAK
|
||||
)
|
||||
|
||||
// Make it easier to directly write to I/O RAM.
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// to define interrupts and to enable/disable them.
|
||||
package interrupt
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// Interrupt provides direct access to hardware interrupts. You can configure
|
||||
// this interrupt through this interface.
|
||||
//
|
||||
@@ -28,6 +30,7 @@ func New(id int, handler func(Interrupt)) Interrupt
|
||||
// individually be enabled/disabled, the compiler should create a pseudo-call
|
||||
// (like runtime/interrupt.use()) that keeps the interrupt alive.
|
||||
type handle struct {
|
||||
handler func(Interrupt)
|
||||
context unsafe.Pointer
|
||||
funcPtr uintptr
|
||||
Interrupt
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func handleInterrupt() {
|
||||
riscv.MSTATUS.SetBits(0x8)
|
||||
|
||||
// Call registered interrupt handler(s)
|
||||
callInterruptHandler(int(interruptNumber))
|
||||
esp.HandleInterrupt(int(interruptNumber))
|
||||
|
||||
// disable CPU interrupts
|
||||
riscv.MSTATUS.ClearBits(0x8)
|
||||
@@ -107,8 +107,3 @@ func handleException(mcause uintptr) {
|
||||
riscv.Asm("wfi")
|
||||
}
|
||||
}
|
||||
|
||||
// callInterruptHandler is a compiler-generated function that calls the
|
||||
// appropriate interrupt handler for the given interrupt ID.
|
||||
//go:linkname callInterruptHandler runtime.callInterruptHandler
|
||||
func callInterruptHandler(id int)
|
||||
|
||||
@@ -7,6 +7,23 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
IRQ_VBLANK = 0
|
||||
IRQ_HBLANK = 1
|
||||
IRQ_VCOUNT = 2
|
||||
IRQ_TIMER0 = 3
|
||||
IRQ_TIMER1 = 4
|
||||
IRQ_TIMER2 = 5
|
||||
IRQ_TIMER3 = 6
|
||||
IRQ_COM = 7
|
||||
IRQ_DMA0 = 8
|
||||
IRQ_DMA1 = 9
|
||||
IRQ_DMA2 = 10
|
||||
IRQ_DMA3 = 11
|
||||
IRQ_KEYPAD = 12
|
||||
IRQ_GAMEPAK = 13
|
||||
)
|
||||
|
||||
var (
|
||||
regInterruptEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000200)))
|
||||
regInterruptRequestFlags = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000202)))
|
||||
@@ -30,10 +47,44 @@ func handleInterrupt() {
|
||||
}
|
||||
}
|
||||
|
||||
// callInterruptHandler is a compiler-generated function that calls the
|
||||
// appropriate interrupt handler for the given interrupt ID.
|
||||
//go:linkname callInterruptHandler runtime.callInterruptHandler
|
||||
func callInterruptHandler(id int)
|
||||
// Pseudo function call that is replaced by the compiler with the actual
|
||||
// functions registered through interrupt.New. If there are none, calls will be
|
||||
// replaced with 'unreachablecalls will be replaced with 'unreachable'.
|
||||
//go:linkname callHandlers runtime/interrupt.callHandlers
|
||||
func callHandlers(num int)
|
||||
|
||||
func callInterruptHandler(id int) {
|
||||
switch id {
|
||||
case IRQ_VBLANK:
|
||||
callHandlers(IRQ_VBLANK)
|
||||
case IRQ_HBLANK:
|
||||
callHandlers(IRQ_HBLANK)
|
||||
case IRQ_VCOUNT:
|
||||
callHandlers(IRQ_VCOUNT)
|
||||
case IRQ_TIMER0:
|
||||
callHandlers(IRQ_TIMER0)
|
||||
case IRQ_TIMER1:
|
||||
callHandlers(IRQ_TIMER1)
|
||||
case IRQ_TIMER2:
|
||||
callHandlers(IRQ_TIMER2)
|
||||
case IRQ_TIMER3:
|
||||
callHandlers(IRQ_TIMER3)
|
||||
case IRQ_COM:
|
||||
callHandlers(IRQ_COM)
|
||||
case IRQ_DMA0:
|
||||
callHandlers(IRQ_DMA0)
|
||||
case IRQ_DMA1:
|
||||
callHandlers(IRQ_DMA1)
|
||||
case IRQ_DMA2:
|
||||
callHandlers(IRQ_DMA2)
|
||||
case IRQ_DMA3:
|
||||
callHandlers(IRQ_DMA3)
|
||||
case IRQ_KEYPAD:
|
||||
callHandlers(IRQ_KEYPAD)
|
||||
case IRQ_GAMEPAK:
|
||||
callHandlers(IRQ_GAMEPAK)
|
||||
}
|
||||
}
|
||||
|
||||
// State represents the previous global interrupt state.
|
||||
type State uint8
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// +build avr cortexm
|
||||
|
||||
package interrupt
|
||||
|
||||
// Register is used to declare an interrupt. You should not normally call this
|
||||
// function: it is only for telling the compiler about the mapping between an
|
||||
// interrupt number and the interrupt handler name.
|
||||
func Register(id int, handlerName string) int
|
||||
@@ -65,7 +65,7 @@ func handleInterrupt() {
|
||||
// Claim this interrupt.
|
||||
id := sifive.PLIC.CLAIM.Get()
|
||||
// Call the interrupt handler, if any is registered for this ID.
|
||||
callInterruptHandler(int(id))
|
||||
sifive.HandleInterrupt(int(id))
|
||||
// Complete this interrupt.
|
||||
sifive.PLIC.CLAIM.Set(id)
|
||||
}
|
||||
@@ -147,7 +147,3 @@ func handleException(code uint) {
|
||||
println()
|
||||
abort()
|
||||
}
|
||||
|
||||
// callInterruptHandler is a compiler-generated function that calls the
|
||||
// appropriate interrupt handler for the given interrupt ID.
|
||||
func callInterruptHandler(id int)
|
||||
|
||||
@@ -85,7 +85,7 @@ func handleInterrupt() {
|
||||
// Claim this interrupt.
|
||||
id := kendryte.PLIC.TARGETS[hartId].CLAIM.Get()
|
||||
// Call the interrupt handler, if any is registered for this ID.
|
||||
callInterruptHandler(int(id))
|
||||
kendryte.HandleInterrupt(int(id))
|
||||
// Complete this interrupt.
|
||||
kendryte.PLIC.TARGETS[hartId].CLAIM.Set(id)
|
||||
}
|
||||
@@ -153,7 +153,3 @@ func handleException(code uint64) {
|
||||
println()
|
||||
abort()
|
||||
}
|
||||
|
||||
// callInterruptHandler is a compiler-generated function that calls the
|
||||
// appropriate interrupt handler for the given interrupt ID.
|
||||
func callInterruptHandler(id int)
|
||||
|
||||
Reference in New Issue
Block a user