aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-11-03 01:24:25 +0100
committerRon Evans <[email protected]>2021-11-06 09:40:15 +0100
commitedcece33caeb0453c105efad799a94239c128dff (patch)
tree5bc4d51da363f5b8a5ebb84bed2879eb2534bddf /tools
parent30bbdd5aeb6a78388db533da72ebace10545935e (diff)
downloadtinygo-edcece33caeb0453c105efad799a94239c128dff.tar.gz
tinygo-edcece33caeb0453c105efad799a94239c128dff.zip
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.
Diffstat (limited to 'tools')
-rwxr-xr-xtools/gen-device-avr/gen-device-avr.go18
-rwxr-xr-xtools/gen-device-svd/gen-device-svd.go46
2 files changed, 47 insertions, 17 deletions
diff --git a/tools/gen-device-avr/gen-device-avr.go b/tools/gen-device-avr/gen-device-avr.go
index 9b9c627ae..c553ec30d 100755
--- a/tools/gen-device-avr/gen-device-avr.go
+++ b/tools/gen-device-avr/gen-device-avr.go
@@ -288,7 +288,6 @@ func writeGo(outdir string, device *Device) error {
package {{.pkgName}}
import (
- "runtime/interrupt"
"runtime/volatile"
"unsafe"
)
@@ -306,11 +305,18 @@ const ({{range .interrupts}}
IRQ_max = {{.interruptMax}} // Highest interrupt number on this device.
)
-// Map interrupt numbers to function names.
-// These aren't real calls, they're removed by the compiler.
-var ({{range .interrupts}}
- _ = interrupt.Register(IRQ_{{.Name}}, "__vector_{{.Name}}"){{end}}
-)
+// Pseudo function call that is replaced by the compiler with the actual
+// functions registered through interrupt.New.
+//go:linkname callHandlers runtime/interrupt.callHandlers
+func callHandlers(num int)
+
+{{- range .interrupts}}
+//export __vector_{{.Name}}
+//go:interrupt
+func interrupt{{.Name}}() {
+ callHandlers(IRQ_{{.Name}})
+}
+{{- end}}
// Peripherals.
var ({{range .peripherals}}
diff --git a/tools/gen-device-svd/gen-device-svd.go b/tools/gen-device-svd/gen-device-svd.go
index 752286f41..e50ff299f 100755
--- a/tools/gen-device-svd/gen-device-svd.go
+++ b/tools/gen-device-svd/gen-device-svd.go
@@ -831,6 +831,15 @@ func writeGo(outdir string, device *Device, interruptSystem string) error {
}
}
+ interruptHandlerMap := make(map[string]*Interrupt)
+ var interruptHandlers []*Interrupt
+ for _, intr := range device.Interrupts {
+ if _, ok := interruptHandlerMap[intr.HandlerName]; !ok {
+ interruptHandlerMap[intr.HandlerName] = intr
+ interruptHandlers = append(interruptHandlers, intr)
+ }
+ }
+
t := template.Must(template.New("go").Funcs(template.FuncMap{
"bytesNeeded": func(i, j uint64) uint64 { return j - i },
"isMultiline": isMultiline,
@@ -846,7 +855,6 @@ func writeGo(outdir string, device *Device, interruptSystem string) error {
package {{.pkgName}}
import (
-{{if eq .interruptSystem "hardware"}}"runtime/interrupt"{{end}}
"runtime/volatile"
"unsafe"
)
@@ -876,14 +884,29 @@ const (
IRQ_max = {{.interruptMax}}
)
+// Pseudo function call that is replaced by the compiler with the actual
+// functions registered through interrupt.New.
+//go:linkname callHandlers runtime/interrupt.callHandlers
+func callHandlers(num int)
+
{{- if eq .interruptSystem "hardware"}}
-// Map interrupt numbers to function names.
-// These aren't real calls, they're removed by the compiler.
-var (
-{{- range .device.Interrupts}}
- _ = interrupt.Register(IRQ_{{.Name}}, "{{.HandlerName}}")
+{{- range .interruptHandlers}}
+//export {{.HandlerName}}
+func interrupt{{.Name}}() {
+ callHandlers(IRQ_{{.Name}})
+}
{{- end}}
-)
+{{- end}}
+
+{{- if eq .interruptSystem "software"}}
+func HandleInterrupt(num int) {
+ switch num {
+ {{- range .interruptHandlers}}
+ case IRQ_{{.Name}}:
+ callHandlers(IRQ_{{.Name}})
+ {{- end}}
+ }
+}
{{- end}}
// Peripherals.
@@ -901,10 +924,11 @@ var (
`))
err = t.Execute(w, map[string]interface{}{
- "device": device,
- "pkgName": filepath.Base(strings.TrimRight(outdir, "/")),
- "interruptMax": maxInterruptValue,
- "interruptSystem": interruptSystem,
+ "device": device,
+ "pkgName": filepath.Base(strings.TrimRight(outdir, "/")),
+ "interruptMax": maxInterruptValue,
+ "interruptSystem": interruptSystem,
+ "interruptHandlers": interruptHandlers,
})
if err != nil {
return err