mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 03:53:42 +00:00
WIP interrupts via ptrtoint handler
This commit is contained in:
@@ -1309,6 +1309,8 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
||||
return c.emitVolatileLoad(frame, instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
return c.emitVolatileStore(frame, instr)
|
||||
case name == "runtime/interrupt.New":
|
||||
return c.emitInterruptGlobal(frame, instr)
|
||||
}
|
||||
|
||||
targetFunc := c.ir.GetFunction(fn)
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// emitInterruptGlobal creates a new runtime/interrupt.Interrupt struct that
|
||||
// will be lowered to a real interrupt during interrupt lowering.
|
||||
//
|
||||
// This two-stage approach allows unused interrupts to be optimized away if
|
||||
// necessary.
|
||||
func (c *Compiler) emitInterruptGlobal(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
||||
// Get the interrupt number, which must be a compile-time constant.
|
||||
id, ok := instr.Args[0].(*ssa.Const)
|
||||
if !ok {
|
||||
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt ID is not a constant")
|
||||
}
|
||||
|
||||
// Get the func value, which also must be a compile time constant.
|
||||
// Note that bound functions are allowed if the function has a pointer
|
||||
// receiver and is a global. This is rather strict but still allows for
|
||||
// idiomatic Go code.
|
||||
funcValue := c.getValue(frame, instr.Args[1])
|
||||
if funcValue.IsAConstant().IsNil() {
|
||||
// Try to determine the cause of the non-constantness for a nice error
|
||||
// message.
|
||||
switch instr.Args[1].(type) {
|
||||
case *ssa.MakeClosure:
|
||||
// This may also be a bound method.
|
||||
return llvm.Value{}, c.makeError(instr.Pos(), "closures are not supported in interrupt.New")
|
||||
}
|
||||
// Fall back to a generic error.
|
||||
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt function must be constant")
|
||||
}
|
||||
|
||||
// Create a new global of type runtime/interrupt.handle. Globals of this
|
||||
// type are lowered in the interrupt lowering pass.
|
||||
globalType := c.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type()
|
||||
globalLLVMType := c.getLLVMType(globalType)
|
||||
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
||||
if global := c.mod.NamedGlobal(globalName); !global.IsNil() {
|
||||
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt redeclared in this program")
|
||||
}
|
||||
global := llvm.AddGlobal(c.mod, globalLLVMType, globalName)
|
||||
global.SetLinkage(llvm.PrivateLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
initializer := llvm.ConstNull(globalLLVMType)
|
||||
initializer = llvm.ConstInsertValue(initializer, funcValue, []uint32{0})
|
||||
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(c.intType, uint64(id.Int64()), true), []uint32{1, 0})
|
||||
global.SetInitializer(initializer)
|
||||
|
||||
// Add debug info to the interrupt global.
|
||||
if c.Debug() {
|
||||
pos := c.ir.Program.Fset.Position(instr.Pos())
|
||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
||||
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
|
||||
LinkageName: globalName,
|
||||
File: c.getDIFile(pos.Filename),
|
||||
Line: pos.Line,
|
||||
Type: c.getDIType(globalType),
|
||||
Expr: c.dibuilder.CreateExpression(nil),
|
||||
LocalToUnit: false,
|
||||
})
|
||||
global.AddMetadata(0, diglobal)
|
||||
}
|
||||
|
||||
// Create the runtime/interrupt.Interrupt type. It is a struct with a single
|
||||
// member of type int.
|
||||
num := llvm.ConstPtrToInt(global, c.intType)
|
||||
interrupt := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num})
|
||||
|
||||
// Add dummy "use" call for AVR, because interrupts may be used even though
|
||||
// they are never referenced again. This is unlike Cortex-M or the RISC-V
|
||||
// PLIC where each interrupt must be enabled using the interrupt number, and
|
||||
// thus keeps the Interrupt object alive.
|
||||
// This call is removed during interrupt lowering.
|
||||
if strings.HasPrefix(c.Triple(), "avr") {
|
||||
useFn := c.mod.NamedFunction("runtime/interrupt.use")
|
||||
if useFn.IsNil() {
|
||||
useFnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
||||
useFn = llvm.AddFunction(c.mod, "runtime/interrupt.use", useFnType)
|
||||
}
|
||||
c.builder.CreateCall(useFn, []llvm.Value{interrupt}, "")
|
||||
}
|
||||
|
||||
return interrupt, nil
|
||||
}
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// UART1 on the Arduino Nano 33 connects to the onboard NINA-W102 WiFi chip.
|
||||
var (
|
||||
@@ -13,11 +16,6 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM3_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
}
|
||||
|
||||
// UART2 on the Arduino Nano 33 connects to the normal TX/RX pins.
|
||||
var (
|
||||
UART2 = UART{
|
||||
@@ -27,11 +25,10 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM5_IRQHandler
|
||||
func handleUART2() {
|
||||
// should reset IRQ
|
||||
UART2.Receive(byte((UART2.Bus.DATA.Get() & 0xFF)))
|
||||
UART2.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||
func init() {
|
||||
// Work around circular definitions.
|
||||
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM3, UART1.handleInterrupt)
|
||||
UART2.interrupt = interrupt.New(sam.IRQ_SERCOM5, UART2.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the Arduino Nano 33.
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/stm32"
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// https://wiki.stm32duino.com/index.php?title=File:Bluepillpinout.gif
|
||||
const (
|
||||
@@ -61,14 +64,12 @@ var (
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
IRQVal: stm32.IRQ_USART1,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
|
||||
//go:export USART1_IRQHandler
|
||||
func handleUART1() {
|
||||
UART1.Receive(byte((UART1.Bus.DR.Get() & 0xFF)))
|
||||
func init() {
|
||||
UART0.interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// UART1 on the Circuit Playground Express.
|
||||
var (
|
||||
@@ -13,9 +16,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
func init() {
|
||||
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM4, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the Circuit Playground Express.
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
@@ -60,9 +63,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
func init() {
|
||||
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -4,6 +4,7 @@ package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
@@ -62,9 +63,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
func init() {
|
||||
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/stm32"
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const (
|
||||
PA0 = portA + 0
|
||||
@@ -100,14 +103,12 @@ var (
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART2,
|
||||
IRQVal: stm32.IRQ_USART2,
|
||||
}
|
||||
UART2 = &UART0
|
||||
)
|
||||
|
||||
//go:export USART2_IRQHandler
|
||||
func handleUART2() {
|
||||
UART2.Receive(byte((UART2.Bus.DR.Get() & 0xFF)))
|
||||
func init() {
|
||||
UART0.interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
@@ -51,9 +54,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
func init() {
|
||||
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM0, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -293,9 +293,10 @@ func waitADCSync() {
|
||||
|
||||
// UART on the SAMD21.
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *sam.SERCOM_USART_Type
|
||||
SERCOM uint8
|
||||
Buffer *RingBuffer
|
||||
Bus *sam.SERCOM_USART_Type
|
||||
SERCOM uint8
|
||||
interrupt interrupt.Interrupt
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -402,10 +403,7 @@ func (uart UART) Configure(config UARTConfig) error {
|
||||
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
|
||||
|
||||
// Enable RX IRQ.
|
||||
// IRQ lines are in the same order as SERCOM instance numbers on SAMD21
|
||||
// chips, so the IRQ number can be trivially determined from the SERCOM
|
||||
// number.
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM0 + uint32(uart.SERCOM))
|
||||
uart.interrupt.Enable()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -433,11 +431,12 @@ func (uart UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// defaultUART1Handler handles the UART1 IRQ.
|
||||
func defaultUART1Handler() {
|
||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||
// this UART instance.
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
// should reset IRQ
|
||||
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
|
||||
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
// I2C on the SAMD21.
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -1402,11 +1403,11 @@ func (usbcdc USBCDC) Configure(config UARTConfig) {
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
arm.EnableIRQ(sam.IRQ_USB_OTHER)
|
||||
arm.EnableIRQ(sam.IRQ_USB_SOF_HSOF)
|
||||
arm.EnableIRQ(sam.IRQ_USB_TRCPT0)
|
||||
arm.EnableIRQ(sam.IRQ_USB_TRCPT1)
|
||||
// enable IRQ at highest priority
|
||||
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
@@ -1452,27 +1453,7 @@ func handlePadCalibration() {
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
//go:export USB_OTHER_IRQHandler
|
||||
func handleUSBOther() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
//go:export USB_SOF_HSOF_IRQHandler
|
||||
func handleUSBSOFHSOF() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
//go:export USB_TRCPT0_IRQHandler
|
||||
func handleUSBTRCPT0() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
//go:export USB_TRCPT1_IRQHandler
|
||||
func handleUSBTRCPT1() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
func handleUSBIRQ() {
|
||||
func handleUSBIRQ(interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
@@ -5,9 +5,9 @@ package machine
|
||||
// Peripheral abstraction layer for the stm32.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/stm32"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
func CPUFrequency() uint32 {
|
||||
@@ -111,9 +111,9 @@ func (p Pin) Get() bool {
|
||||
|
||||
// UART
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *stm32.USART_Type
|
||||
IRQVal uint32
|
||||
Buffer *RingBuffer
|
||||
Bus *stm32.USART_Type
|
||||
interrupt interrupt.Interrupt
|
||||
}
|
||||
|
||||
// Configure the UART.
|
||||
@@ -155,8 +155,8 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||
|
||||
// Enable RX IRQ
|
||||
arm.SetPriority(uart.IRQVal, 0xc0)
|
||||
arm.EnableIRQ(uart.IRQVal)
|
||||
uart.interrupt.SetPriority(0xc0)
|
||||
uart.interrupt.Enable()
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART.
|
||||
@@ -182,6 +182,12 @@ func (uart UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||
// this UART instance.
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
uart.Receive(byte((uart.Bus.DR.Get() & 0xFF)))
|
||||
}
|
||||
|
||||
// SPI on the STM32.
|
||||
type SPI struct {
|
||||
Bus *stm32.SPI_Type
|
||||
|
||||
@@ -5,8 +5,8 @@ package machine
|
||||
// Peripheral abstraction layer for the stm32.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
func CPUFrequency() uint32 {
|
||||
@@ -203,8 +203,11 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
stm32.USART2.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||
|
||||
// Enable RX IRQ.
|
||||
arm.SetPriority(stm32.IRQ_USART2, 0xc0)
|
||||
arm.EnableIRQ(stm32.IRQ_USART2)
|
||||
intr := interrupt.New(stm32.IRQ_USART2, func(interrupt.Interrupt) {
|
||||
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
|
||||
})
|
||||
intr.SetPriority(0xc0)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
@@ -215,8 +218,3 @@ func (uart UART) WriteByte(c byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:export USART2_IRQHandler
|
||||
func handleUSART2() {
|
||||
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
|
||||
}
|
||||
|
||||
@@ -23,3 +23,8 @@ func New(id int, handler func(Interrupt)) Interrupt
|
||||
// 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
|
||||
|
||||
type handle struct {
|
||||
handler func(Interrupt)
|
||||
Interrupt
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"device/arm"
|
||||
"device/stm32"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
@@ -101,8 +102,9 @@ func initRTC() {
|
||||
func initTIM() {
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||
|
||||
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
|
||||
arm.EnableIRQ(stm32.IRQ_TIM3)
|
||||
intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3)
|
||||
intr.SetPriority(0xc3)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
@@ -186,8 +188,7 @@ func timerSleep(ticks uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
//go:export TIM3_IRQHandler
|
||||
func handleTIM3() {
|
||||
func handleTIM3(interrupt.Interrupt) {
|
||||
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||
// Disable the timer.
|
||||
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"device/arm"
|
||||
"device/stm32"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
@@ -121,8 +122,9 @@ var timerWakeup volatile.Register8
|
||||
func initTIM3() {
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||
|
||||
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
|
||||
arm.EnableIRQ(stm32.IRQ_TIM3)
|
||||
intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3)
|
||||
intr.SetPriority(0xc3)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
// Enable the TIM7 clock.(tick count)
|
||||
@@ -139,8 +141,9 @@ func initTIM7() {
|
||||
// Enable the timer.
|
||||
stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN)
|
||||
|
||||
arm.SetPriority(stm32.IRQ_TIM7, 0xc1)
|
||||
arm.EnableIRQ(stm32.IRQ_TIM7)
|
||||
intr := interrupt.New(stm32.IRQ_TIM7, handleTIM7)
|
||||
intr.SetPriority(0xc1)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
@@ -183,8 +186,7 @@ func timerSleep(ticks uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
//go:export TIM3_IRQHandler
|
||||
func handleTIM3() {
|
||||
func handleTIM3(interrupt.Interrupt) {
|
||||
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||
// Disable the timer.
|
||||
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
|
||||
@@ -197,8 +199,7 @@ func handleTIM3() {
|
||||
}
|
||||
}
|
||||
|
||||
//go:export TIM7_IRQHandler
|
||||
func handleTIM7() {
|
||||
func handleTIM7(interrupt.Interrupt) {
|
||||
if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||
// clear the update flag
|
||||
stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF)
|
||||
|
||||
+50
-87
@@ -47,52 +47,43 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
|
||||
call.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
create := mod.NamedFunction("runtime/interrupt.New")
|
||||
if create.IsNil() {
|
||||
// No interrupt handlers to create.
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx := mod.Context()
|
||||
nullptr := llvm.ConstNull(llvm.PointerType(ctx.Int8Type(), 0))
|
||||
builder := ctx.NewBuilder()
|
||||
defer builder.Dispose()
|
||||
|
||||
var dibuilder *llvm.DIBuilder
|
||||
|
||||
// Create a function type with the signature of an interrupt handler.
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), nil, false)
|
||||
|
||||
for _, call := range getUses(create) {
|
||||
if call.IsACallInst().IsNil() {
|
||||
errs = append(errs, errorAt(call, "expected a call to runtime/interrupt.New"))
|
||||
continue
|
||||
}
|
||||
if call.OperandsCount() != 6 {
|
||||
// 6 params:
|
||||
// * 1 for the IRQ number
|
||||
// * 2 for the function
|
||||
// * 2 extra: context and parentHandle
|
||||
// * one more for the called value?
|
||||
errs = append(errs, errorAt(call, fmt.Sprintf("unexpected interrupt.New signature, expected 6 operands, got %d", call.OperandsCount())))
|
||||
continue
|
||||
}
|
||||
num := call.Operand(0)
|
||||
if num.IsAConstant().IsNil() {
|
||||
errs = append(errs, errorAt(call, "non-constant interrupt number"))
|
||||
handleType := mod.GetTypeByName("runtime/interrupt.handle")
|
||||
if handleType.IsNil() {
|
||||
// Nothing to do here.
|
||||
return errs
|
||||
}
|
||||
handlePtrType := llvm.PointerType(handleType, 0)
|
||||
var handlers []llvm.Value
|
||||
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||
if global.Type() != handlePtrType {
|
||||
continue
|
||||
}
|
||||
handlers = append(handlers, global)
|
||||
}
|
||||
|
||||
for _, global := range handlers {
|
||||
initializer := global.Initializer()
|
||||
num := llvm.ConstExtractValue(initializer, []uint32{1, 0})
|
||||
name := handlerNames[num.SExtValue()]
|
||||
|
||||
if name == "" {
|
||||
errs = append(errs, errorAt(call, fmt.Sprintf("cannot find interrupt name for number %d", num.SExtValue())))
|
||||
errs = append(errs, errorAt(global, fmt.Sprintf("cannot find interrupt name for number %d", num.SExtValue())))
|
||||
continue
|
||||
}
|
||||
|
||||
// Create the func value.
|
||||
handlerContext := call.Operand(1)
|
||||
handlerFuncPtr := call.Operand(2)
|
||||
handlerContext := llvm.ConstExtractValue(initializer, []uint32{0, 0})
|
||||
handlerFuncPtr := llvm.ConstExtractValue(initializer, []uint32{0, 1})
|
||||
if isFunctionLocal(handlerContext) || isFunctionLocal(handlerFuncPtr) {
|
||||
errs = append(errs, errorAt(call, "func value must be constant"))
|
||||
errs = append(errs, errorAt(global, "func value must be constant"))
|
||||
continue
|
||||
}
|
||||
if !handlerFuncPtr.IsAConstantExpr().IsNil() && handlerFuncPtr.Opcode() == llvm.PtrToInt {
|
||||
@@ -100,23 +91,23 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
|
||||
// switch statement.
|
||||
global := handlerFuncPtr.Operand(0)
|
||||
if global.IsAGlobalValue().IsNil() {
|
||||
errs = append(errs, errorAt(call, "internal error: expected a global for func lowering"))
|
||||
errs = append(errs, errorAt(global, "internal error: expected a global for func lowering"))
|
||||
continue
|
||||
}
|
||||
initializer := global.Initializer()
|
||||
if initializer.Type() != mod.GetTypeByName("runtime.funcValueWithSignature") {
|
||||
errs = append(errs, errorAt(call, "internal error: func lowering global has unexpected type"))
|
||||
errs = append(errs, errorAt(global, "internal error: func lowering global has unexpected type"))
|
||||
continue
|
||||
}
|
||||
ptrtoint := llvm.ConstExtractValue(initializer, []uint32{0})
|
||||
if ptrtoint.IsAConstantExpr().IsNil() || ptrtoint.Opcode() != llvm.PtrToInt {
|
||||
errs = append(errs, errorAt(call, "internal error: func lowering global has unexpected func ptr type"))
|
||||
errs = append(errs, errorAt(global, "internal error: func lowering global has unexpected func ptr type"))
|
||||
continue
|
||||
}
|
||||
handlerFuncPtr = ptrtoint.Operand(0)
|
||||
}
|
||||
if handlerFuncPtr.Type().TypeKind() != llvm.PointerTypeKind || handlerFuncPtr.Type().ElementType().TypeKind() != llvm.FunctionTypeKind {
|
||||
errs = append(errs, errorAt(call, "internal error: unexpected LLVM types in func value"))
|
||||
errs = append(errs, errorAt(global, "internal error: unexpected LLVM types in func value"))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -129,14 +120,14 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
|
||||
// Don't bother with a precise error message (listing the
|
||||
// previsous location) because this should not normally happen
|
||||
// anyway.
|
||||
errs = append(errs, errorAt(call, name+" redeclared with a different signature"))
|
||||
errs = append(errs, errorAt(global, name+" redeclared with a different signature"))
|
||||
continue
|
||||
} else if fn.IsDeclaration() {
|
||||
} else if !fn.IsDeclaration() {
|
||||
// Interrupt handler was already defined. Check the first
|
||||
// instruction (which should be a call) whether this handler would
|
||||
// be identical anyway.
|
||||
firstInst := fn.FirstBasicBlock().FirstInstruction()
|
||||
if !firstInst.IsACallInst().IsNil() && firstInst.OperandsCount() == 4 && firstInst.Operand(0) == num && firstInst.Operand(1) != handlerContext {
|
||||
if !firstInst.IsACallInst().IsNil() && firstInst.OperandsCount() == 4 && firstInst.CalledValue() == handlerFuncPtr && firstInst.Operand(0) == num && firstInst.Operand(1) == handlerContext {
|
||||
// Already defined and apparently identical, so assume this is
|
||||
// fine.
|
||||
continue
|
||||
@@ -147,7 +138,7 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
|
||||
if fnPos.IsValid() {
|
||||
errValue += "\n\tprevious declaration at " + fnPos.String()
|
||||
}
|
||||
errs = append(errs, errorAt(call, errValue))
|
||||
errs = append(errs, errorAt(global, errValue))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -161,61 +152,33 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
|
||||
fn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
||||
}
|
||||
|
||||
// Attach a proper debug location to this function.
|
||||
// Use the location of the call instruction, because as far as the user
|
||||
// is concerned that is where the interrupt is defined. This is
|
||||
// especially relevant for multiple definition errors, where you'd
|
||||
// really want the previous interrupt.New call location to be used for
|
||||
// easy debugging.
|
||||
loc := call.InstructionDebugLoc()
|
||||
if !loc.IsNil() {
|
||||
if dibuilder == nil {
|
||||
dibuilder = llvm.NewDIBuilder(mod)
|
||||
defer func() {
|
||||
dibuilder.Finalize()
|
||||
dibuilder.Destroy()
|
||||
}()
|
||||
// Must create a new compile unit for some reason.
|
||||
dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||
File: "<interrupt-lowering>",
|
||||
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
||||
Producer: "TinyGo",
|
||||
Optimized: true,
|
||||
})
|
||||
}
|
||||
|
||||
// Attach debug info to the function.
|
||||
file := loc.LocationScope().ScopeFile()
|
||||
diFnType := dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||
File: file,
|
||||
})
|
||||
difunc := dibuilder.CreateFunction(file, llvm.DIFunction{
|
||||
Name: name,
|
||||
LinkageName: name,
|
||||
File: file,
|
||||
Line: int(loc.LocationLine()),
|
||||
Type: diFnType,
|
||||
LocalToUnit: false,
|
||||
IsDefinition: true,
|
||||
ScopeLine: 0,
|
||||
Flags: llvm.FlagPrototyped,
|
||||
Optimized: true,
|
||||
})
|
||||
fn.SetSubprogram(difunc)
|
||||
|
||||
// Use the same location inside the thunk.
|
||||
builder.SetCurrentDebugLocation(loc.LocationLine(), loc.LocationColumn(), difunc, llvm.Metadata{})
|
||||
|
||||
}
|
||||
|
||||
// Fill the function declaration with the forwarding call.
|
||||
builder.CreateCall(handlerFuncPtr, []llvm.Value{num, handlerContext, nullptr}, "")
|
||||
builder.CreateRetVoid()
|
||||
|
||||
// Replace the function call.
|
||||
interruptValue := llvm.ConstNamedStruct(mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num})
|
||||
call.ReplaceAllUsesWith(interruptValue)
|
||||
for _, user := range getUses(global) {
|
||||
if user.IsAConstantExpr().IsNil() || user.Opcode() != llvm.PtrToInt {
|
||||
errs = append(errs, errorAt(global, "internal error: expected a ptrtoint"))
|
||||
continue
|
||||
}
|
||||
user.ReplaceAllUsesWith(num)
|
||||
}
|
||||
global.EraseFromParentAsGlobal()
|
||||
}
|
||||
|
||||
// Remove now-useless runtime/interrupt.use calls. These are used for some
|
||||
// platforms like AVR that do not need to enable interrupts to use them, so
|
||||
// need another way to keep them alive.
|
||||
// After interrupts have been lowered, this call is useless and would cause
|
||||
// a linker error so must be removed.
|
||||
for _, call := range getUses(mod.NamedFunction("runtime/interrupt.use")) {
|
||||
if call.IsACallInst().IsNil() {
|
||||
errs = append(errs, errorAt(call, "internal error: expected call to runtime/interrupt.use"))
|
||||
continue
|
||||
}
|
||||
|
||||
call.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user