all: add compiler support for interrupts
This commit lets the compiler know about interrupts and allows optimizations to be performed based on that: interrupts are eliminated when they appear to be unused in a program. This is done with a new pseudo-call (runtime/interrupt.New) that is treated specially by the compiler.
Этот коммит содержится в:
родитель
3729fcfa9e
коммит
a5ed993f8d
36 изменённых файлов: 766 добавлений и 199 удалений
|
@ -217,7 +217,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||
path = path[len(tinygoPath+"/src/"):]
|
||||
}
|
||||
switch path {
|
||||
case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync", "testing", "internal/reflectlite":
|
||||
case "machine", "os", "reflect", "runtime", "runtime/interrupt", "runtime/volatile", "sync", "testing", "internal/reflectlite":
|
||||
return path
|
||||
default:
|
||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||
|
@ -828,9 +828,6 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
|||
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
||||
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
||||
}
|
||||
if frame.fn.IsInterrupt() && strings.HasPrefix(c.Triple(), "avr") {
|
||||
frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
||||
}
|
||||
|
||||
// Some functions have a pragma controlling the inlining level.
|
||||
switch frame.fn.Inline() {
|
||||
|
@ -1314,6 +1311,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)
|
||||
|
|
92
compiler/interrupt.go
Обычный файл
92
compiler/interrupt.go
Обычный файл
|
@ -0,0 +1,92 @@
|
|||
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)
|
||||
global.SetUnnamedAddr(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
|
||||
}
|
|
@ -57,6 +57,12 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []er
|
|||
transform.OptimizeStringToBytes(c.mod)
|
||||
transform.OptimizeAllocs(c.mod)
|
||||
transform.LowerInterfaces(c.mod)
|
||||
|
||||
errs := transform.LowerInterrupts(c.mod)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
if c.funcImplementation() == funcValueSwitch {
|
||||
transform.LowerFuncValues(c.mod)
|
||||
}
|
||||
|
@ -100,6 +106,10 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []er
|
|||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
errs := transform.LowerInterrupts(c.mod)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
}
|
||||
if c.VerifyIR() {
|
||||
if errs := c.checkModule(); errs != nil {
|
||||
|
|
35
ir/ir.go
35
ir/ir.go
|
@ -27,14 +27,13 @@ type Program struct {
|
|||
// Function or method.
|
||||
type Function struct {
|
||||
*ssa.Function
|
||||
LLVMFn llvm.Value
|
||||
module string // go:wasm-module
|
||||
linkName string // go:linkname, go:export, go:interrupt
|
||||
exported bool // go:export
|
||||
nobounds bool // go:nobounds
|
||||
flag bool // used by dead code elimination
|
||||
interrupt bool // go:interrupt
|
||||
inline InlineType // go:inline
|
||||
LLVMFn llvm.Value
|
||||
module string // go:wasm-module
|
||||
linkName string // go:linkname, go:export
|
||||
exported bool // go:export
|
||||
nobounds bool // go:nobounds
|
||||
flag bool // used by dead code elimination
|
||||
inline InlineType // go:inline
|
||||
}
|
||||
|
||||
// Interface type that is at some point used in a type assert (to check whether
|
||||
|
@ -243,18 +242,6 @@ func (f *Function) parsePragmas() {
|
|||
f.inline = InlineHint
|
||||
case "//go:noinline":
|
||||
f.inline = InlineNone
|
||||
case "//go:interrupt":
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
name := parts[1]
|
||||
if strings.HasSuffix(name, "_vect") {
|
||||
// AVR vector naming
|
||||
name = "__vector_" + name[:len(name)-5]
|
||||
}
|
||||
f.linkName = name
|
||||
f.exported = true
|
||||
f.interrupt = true
|
||||
case "//go:linkname":
|
||||
if len(parts) != 3 || parts[1] != f.Name() {
|
||||
continue
|
||||
|
@ -288,14 +275,6 @@ func (f *Function) IsExported() bool {
|
|||
return f.exported || f.CName() != ""
|
||||
}
|
||||
|
||||
// Return true for functions annotated with //go:interrupt. The function name is
|
||||
// already customized in LinkName() to hook up in the interrupt vector.
|
||||
//
|
||||
// On some platforms (like AVR), interrupts need a special compiler flag.
|
||||
func (f *Function) IsInterrupt() bool {
|
||||
return f.interrupt
|
||||
}
|
||||
|
||||
// Return the inline directive of this function.
|
||||
func (f *Function) Inline() InlineType {
|
||||
return f.inline
|
||||
|
|
|
@ -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,9 @@ 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() {
|
||||
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
|
||||
|
|
|
@ -4,6 +4,7 @@ package machine
|
|||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
|
@ -232,6 +233,18 @@ func (uart UART) Configure(config UARTConfig) {
|
|||
config.BaudRate = 9600
|
||||
}
|
||||
|
||||
// Register the UART interrupt.
|
||||
interrupt.New(avr.IRQ_USART_RX, func(intr interrupt.Interrupt) {
|
||||
// Read register to clear it.
|
||||
data := avr.UDR0.Get()
|
||||
|
||||
// Ensure no error.
|
||||
if !avr.UCSR0A.HasBits(avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0) {
|
||||
// Put data from UDR register into buffer.
|
||||
UART0.Receive(byte(data))
|
||||
}
|
||||
})
|
||||
|
||||
// Set baud rate based on prescale formula from
|
||||
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
||||
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
||||
|
@ -254,15 +267,3 @@ func (uart UART) WriteByte(c byte) error {
|
|||
avr.UDR0.Set(c) // send char
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:interrupt USART_RX_vect
|
||||
func handleUSART_RX() {
|
||||
// Read register to clear it.
|
||||
data := avr.UDR0.Get()
|
||||
|
||||
// Ensure no error.
|
||||
if !avr.UCSR0A.HasBits(avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0) {
|
||||
// Put data from UDR register into buffer.
|
||||
UART0.Receive(byte(data))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
@ -292,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 (
|
||||
|
@ -401,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
|
||||
}
|
||||
|
@ -432,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.
|
||||
|
@ -1368,7 +1368,8 @@ func (usbcdc USBCDC) Configure(config UARTConfig) {
|
|||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
arm.EnableIRQ(sam.IRQ_USB)
|
||||
intr := interrupt.New(sam.IRQ_USB, handleUSB)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
|
@ -1414,8 +1415,7 @@ func handlePadCalibration() {
|
|||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
//go:export USB_IRQHandler
|
||||
func handleUSB() {
|
||||
func handleUSB(intr interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
@ -1547,11 +1548,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() {
|
||||
|
@ -1597,27 +1598,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)
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
package machine
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/nrf"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -88,8 +88,9 @@ func (uart UART) Configure(config UARTConfig) {
|
|||
nrf.UART0.INTENSET.Set(nrf.UART_INTENSET_RXDRDY_Msk)
|
||||
|
||||
// Enable RX IRQ.
|
||||
arm.SetPriority(nrf.IRQ_UART0, 0xc0) // low priority
|
||||
arm.EnableIRQ(nrf.IRQ_UART0)
|
||||
intr := interrupt.New(nrf.IRQ_UART0, UART0.handleInterrupt)
|
||||
intr.SetPriority(0xc0) // low priority
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART.
|
||||
|
@ -116,7 +117,7 @@ func (uart UART) WriteByte(c byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (uart UART) handleInterrupt() {
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
if nrf.UART0.EVENTS_RXDRDY.Get() != 0 {
|
||||
uart.Receive(byte(nrf.UART0.RXD.Get()))
|
||||
nrf.UART0.EVENTS_RXDRDY.Set(0x0)
|
||||
|
|
|
@ -20,11 +20,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
|||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
//go:export UART0_IRQHandler
|
||||
func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||
|
|
|
@ -21,11 +21,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
|||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
//go:export UARTE0_UART0_IRQHandler
|
||||
func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||
|
|
|
@ -77,11 +77,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
|||
nrf.UART0.PSEL.RXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
//go:export UARTE0_UART0_IRQHandler
|
||||
func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSEL.SCL.Set(uint32(scl))
|
||||
i2c.Bus.PSEL.SDA.Set(uint32(sda))
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
|
|
38
src/runtime/interrupt/interrupt.go
Обычный файл
38
src/runtime/interrupt/interrupt.go
Обычный файл
|
@ -0,0 +1,38 @@
|
|||
// Package interrupt provides access to hardware interrupts. It provides a way
|
||||
// to define interrupts and to enable/disable them.
|
||||
package interrupt
|
||||
|
||||
// Interrupt provides direct access to hardware interrupts. You can configure
|
||||
// this interrupt through this interface.
|
||||
//
|
||||
// Do not use the zero value of an Interrupt object. Instead, call New to obtain
|
||||
// an interrupt handle.
|
||||
type Interrupt struct {
|
||||
// Make this number unexported so it cannot be set directly. This provides
|
||||
// some encapsulation.
|
||||
num int
|
||||
}
|
||||
|
||||
// New is a compiler intrinsic that creates a new Interrupt object. You may call
|
||||
// it only once, and must pass constant parameters to it. That means that the
|
||||
// interrupt ID must be a Go constant and that the handler must be a simple
|
||||
// function: closures are not supported.
|
||||
func New(id int, handler func(Interrupt)) 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
|
||||
|
||||
// handle is used internally, between IR generation and interrupt lowering. The
|
||||
// frontend will create runtime/interrupt.handle objects, cast them to an int,
|
||||
// and use that in an Interrupt object. That way the compiler will be able to
|
||||
// optimize away all interrupt handles that are never used in a program.
|
||||
// This system only works when interrupts need to be enabled before use and this
|
||||
// is done only through calling Enable() on this object. If interrups cannot
|
||||
// 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)
|
||||
Interrupt
|
||||
}
|
23
src/runtime/interrupt/interrupt_cortexm.go
Обычный файл
23
src/runtime/interrupt/interrupt_cortexm.go
Обычный файл
|
@ -0,0 +1,23 @@
|
|||
// +build cortexm
|
||||
|
||||
package interrupt
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
)
|
||||
|
||||
// Enable enables this interrupt. Right after calling this function, the
|
||||
// interrupt may be invoked if it was already pending.
|
||||
func (irq Interrupt) Enable() {
|
||||
arm.EnableIRQ(uint32(irq.num))
|
||||
}
|
||||
|
||||
// SetPriority sets the interrupt priority for this interrupt. A lower number
|
||||
// means a higher priority. Additionally, most hardware doesn't implement all
|
||||
// priority bits (only the uppoer bits).
|
||||
//
|
||||
// Examples: 0xff (lowest priority), 0xc0 (low priority), 0x00 (highest possible
|
||||
// priority).
|
||||
func (irq Interrupt) SetPriority(priority uint8) {
|
||||
arm.SetPriority(uint32(irq.num), uint32(priority))
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"device/arm"
|
||||
"device/sam"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
@ -214,8 +215,14 @@ func initRTC() {
|
|||
sam.RTC_MODE0.CTRL.SetBits(sam.RTC_MODE0_CTRL_ENABLE)
|
||||
waitForSync()
|
||||
|
||||
arm.SetPriority(sam.IRQ_RTC, 0xc0)
|
||||
arm.EnableIRQ(sam.IRQ_RTC)
|
||||
intr := interrupt.New(sam.IRQ_RTC, func(intr interrupt.Interrupt) {
|
||||
// disable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
|
||||
timerWakeup.Set(1)
|
||||
})
|
||||
intr.SetPriority(0xc0)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
func waitForSync() {
|
||||
|
@ -286,14 +293,6 @@ func timerSleep(ticks uint32) {
|
|||
}
|
||||
}
|
||||
|
||||
//go:export RTC_IRQHandler
|
||||
func handleRTC() {
|
||||
// disable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
|
||||
timerWakeup.Set(1)
|
||||
}
|
||||
|
||||
func initUSBClock() {
|
||||
// Turn on clock for USB
|
||||
sam.PM.APBBMASK.SetBits(sam.PM_APBBMASK_USB_)
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"device/arm"
|
||||
"device/sam"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
|
@ -202,8 +203,14 @@ func initRTC() {
|
|||
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
arm.SetPriority(sam.IRQ_RTC, 0xc0)
|
||||
arm.EnableIRQ(sam.IRQ_RTC)
|
||||
irq := interrupt.New(sam.IRQ_RTC, func(interrupt.Interrupt) {
|
||||
// disable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTFLAG.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
|
||||
timerWakeup.Set(1)
|
||||
})
|
||||
irq.SetPriority(0xc0)
|
||||
irq.Enable()
|
||||
}
|
||||
|
||||
func waitForSync() {
|
||||
|
@ -271,14 +278,6 @@ func timerSleep(ticks uint32) {
|
|||
}
|
||||
}
|
||||
|
||||
//go:export RTC_IRQHandler
|
||||
func handleRTC() {
|
||||
// disable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTFLAG.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
|
||||
timerWakeup.Set(1)
|
||||
}
|
||||
|
||||
func initUSBClock() {
|
||||
// Turn on clock(s) for USB
|
||||
//MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB;
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"device/arm"
|
||||
"device/nrf"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
|
@ -43,8 +44,13 @@ func initLFCLK() {
|
|||
|
||||
func initRTC() {
|
||||
nrf.RTC1.TASKS_START.Set(1)
|
||||
arm.SetPriority(nrf.IRQ_RTC1, 0xc0) // low priority
|
||||
arm.EnableIRQ(nrf.IRQ_RTC1)
|
||||
intr := interrupt.New(nrf.IRQ_RTC1, func(intr interrupt.Interrupt) {
|
||||
nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0)
|
||||
nrf.RTC1.EVENTS_COMPARE[0].Set(0)
|
||||
rtc_wakeup.Set(1)
|
||||
})
|
||||
intr.SetPriority(0xc0) // low priority
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
|
@ -96,10 +102,3 @@ func rtc_sleep(ticks uint32) {
|
|||
arm.Asm("wfi")
|
||||
}
|
||||
}
|
||||
|
||||
//go:export RTC1_IRQHandler
|
||||
func handleRTC1() {
|
||||
nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0)
|
||||
nrf.RTC1.EVENTS_COMPARE[0].Set(0)
|
||||
rtc_wakeup.Set(1)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -260,6 +260,7 @@ func writeGo(outdir string, device *Device) error {
|
|||
package {{.pkgName}}
|
||||
|
||||
import (
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
@ -277,6 +278,12 @@ 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}}
|
||||
)
|
||||
|
||||
// Peripherals.
|
||||
var ({{range .peripherals}}
|
||||
// {{.Caption}}
|
||||
|
|
|
@ -82,6 +82,7 @@ type Device struct {
|
|||
|
||||
type interrupt struct {
|
||||
Name string
|
||||
HandlerName string
|
||||
peripheralIndex int
|
||||
Value int // interrupt number
|
||||
Description string
|
||||
|
@ -171,12 +172,12 @@ func readSVD(path, sourceURL string) (*Device, error) {
|
|||
groupName := cleanName(periphEl.GroupName)
|
||||
|
||||
for _, interrupt := range periphEl.Interrupts {
|
||||
addInterrupt(interrupts, interrupt.Name, interrupt.Index, description)
|
||||
addInterrupt(interrupts, interrupt.Name, interrupt.Name, interrupt.Index, description)
|
||||
// As a convenience, also use the peripheral name as the interrupt
|
||||
// name. Only do that for the nrf for now, as the stm32 .svd files
|
||||
// don't always put interrupts in the correct peripheral...
|
||||
if len(periphEl.Interrupts) == 1 && strings.HasPrefix(device.Name, "nrf") {
|
||||
addInterrupt(interrupts, periphEl.Name, interrupt.Index, description)
|
||||
addInterrupt(interrupts, periphEl.Name, interrupt.Name, interrupt.Index, description)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,7 +389,7 @@ func readSVD(path, sourceURL string) (*Device, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func addInterrupt(interrupts map[string]*interrupt, name string, index int, description string) {
|
||||
func addInterrupt(interrupts map[string]*interrupt, name, interruptName string, index int, description string) {
|
||||
if _, ok := interrupts[name]; ok {
|
||||
if interrupts[name].Value != index {
|
||||
// Note: some SVD files like the one for STM32H7x7 contain mistakes.
|
||||
|
@ -409,6 +410,7 @@ func addInterrupt(interrupts map[string]*interrupt, name string, index int, desc
|
|||
} else {
|
||||
interrupts[name] = &interrupt{
|
||||
Name: name,
|
||||
HandlerName: interruptName + "_IRQHandler",
|
||||
peripheralIndex: len(interrupts),
|
||||
Value: index,
|
||||
Description: description,
|
||||
|
@ -619,6 +621,7 @@ func writeGo(outdir string, device *Device) error {
|
|||
package {{.pkgName}}
|
||||
|
||||
import (
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
@ -628,12 +631,18 @@ const (
|
|||
DEVICE = "{{.metadata.name}}"
|
||||
)
|
||||
|
||||
// Interrupt numbers
|
||||
// Interrupt numbers.
|
||||
const ({{range .interrupts}}
|
||||
IRQ_{{.Name}} = {{.Value}} // {{.Description}}{{end}}
|
||||
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}}, "{{.HandlerName}}"){{end}}
|
||||
)
|
||||
|
||||
// Peripherals.
|
||||
var (
|
||||
{{range .peripherals}} {{.Name}} = (*{{.GroupName}}_Type)(unsafe.Pointer(uintptr(0x{{printf "%x" .BaseAddress}}))) // {{.Description}}
|
||||
|
@ -879,7 +888,7 @@ Default_Handler:
|
|||
num++
|
||||
}
|
||||
num++
|
||||
fmt.Fprintf(w, " .long %s_IRQHandler\n", intr.Name)
|
||||
fmt.Fprintf(w, " .long %s\n", intr.HandlerName)
|
||||
}
|
||||
|
||||
w.WriteString(`
|
||||
|
|
48
transform/errors.go
Обычный файл
48
transform/errors.go
Обычный файл
|
@ -0,0 +1,48 @@
|
|||
package transform
|
||||
|
||||
import (
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// errorAt returns an error value at the location of the value.
|
||||
// The location information may not be complete as it depends on debug
|
||||
// information in the IR.
|
||||
func errorAt(val llvm.Value, msg string) scanner.Error {
|
||||
return scanner.Error{
|
||||
Pos: getPosition(val),
|
||||
Msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// getPosition returns the position information for the given value, as far as
|
||||
// it is available.
|
||||
func getPosition(val llvm.Value) token.Position {
|
||||
if !val.IsAInstruction().IsNil() {
|
||||
loc := val.InstructionDebugLoc()
|
||||
if loc.IsNil() {
|
||||
return token.Position{}
|
||||
}
|
||||
file := loc.LocationScope().ScopeFile()
|
||||
return token.Position{
|
||||
Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
|
||||
Line: int(loc.LocationLine()),
|
||||
Column: int(loc.LocationColumn()),
|
||||
}
|
||||
} else if !val.IsAFunction().IsNil() {
|
||||
loc := val.Subprogram()
|
||||
if loc.IsNil() {
|
||||
return token.Position{}
|
||||
}
|
||||
file := loc.ScopeFile()
|
||||
return token.Position{
|
||||
Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
|
||||
Line: int(loc.SubprogramLine()),
|
||||
}
|
||||
} else {
|
||||
return token.Position{}
|
||||
}
|
||||
}
|
218
transform/interrupt.go
Обычный файл
218
transform/interrupt.go
Обычный файл
|
@ -0,0 +1,218 @@
|
|||
package transform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// LowerInterrupts creates interrupt handlers for the interrupts created by
|
||||
// runtime/interrupt.New.
|
||||
//
|
||||
// The operation is as follows. The compiler creates the following during IR
|
||||
// generation:
|
||||
// * calls to runtime/interrupt.Register that map interrupt IDs to ISR names.
|
||||
// * runtime/interrupt.handle objects that store the (constant) interrupt ID and
|
||||
// interrupt handler func value.
|
||||
//
|
||||
// This pass then creates the specially named interrupt handler names that
|
||||
// simply call the registered handlers. This might seem like it causes extra
|
||||
// overhead, but in fact inlining and const propagation will eliminate most if
|
||||
// not all of that.
|
||||
func LowerInterrupts(mod llvm.Module) []error {
|
||||
var errs []error
|
||||
|
||||
// Discover interrupts. The runtime/interrupt.Register call is a compiler
|
||||
// intrinsic that maps interrupt numbers to handler names.
|
||||
handlerNames := map[int64]string{}
|
||||
for _, call := range getUses(mod.NamedFunction("runtime/interrupt.Register")) {
|
||||
if call.IsACallInst().IsNil() {
|
||||
errs = append(errs, errorAt(call, "expected a call to runtime/interrupt.Register?"))
|
||||
continue
|
||||
}
|
||||
|
||||
num := call.Operand(0)
|
||||
if num.IsAConstant().IsNil() {
|
||||
errs = append(errs, errorAt(call, "non-constant interrupt number?"))
|
||||
continue
|
||||
}
|
||||
|
||||
// extract the interrupt name
|
||||
nameStrGEP := call.Operand(1)
|
||||
if nameStrGEP.IsAConstantExpr().IsNil() || nameStrGEP.Opcode() != llvm.GetElementPtr {
|
||||
errs = append(errs, errorAt(call, "expected a string operand?"))
|
||||
continue
|
||||
}
|
||||
nameStrPtr := nameStrGEP.Operand(0) // note: assuming it's a GEP to the first byte
|
||||
nameStrLen := call.Operand(2)
|
||||
if nameStrPtr.IsAGlobalValue().IsNil() || !nameStrPtr.IsGlobalConstant() || nameStrLen.IsAConstant().IsNil() {
|
||||
errs = append(errs, errorAt(call, "non-constant interrupt name?"))
|
||||
continue
|
||||
}
|
||||
|
||||
// keep track of this name
|
||||
name := string(getGlobalBytes(nameStrPtr)[:nameStrLen.SExtValue()])
|
||||
handlerNames[num.SExtValue()] = name
|
||||
|
||||
// remove this pseudo-call
|
||||
call.ReplaceAllUsesWith(llvm.ConstNull(call.Type()))
|
||||
call.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
ctx := mod.Context()
|
||||
nullptr := llvm.ConstNull(llvm.PointerType(ctx.Int8Type(), 0))
|
||||
builder := ctx.NewBuilder()
|
||||
defer builder.Dispose()
|
||||
|
||||
// Create a function type with the signature of an interrupt handler.
|
||||
fnType := llvm.FunctionType(ctx.VoidType(), nil, false)
|
||||
|
||||
// Collect a slice of interrupt handle objects. The fact that they still
|
||||
// exist in the IR indicates that they could not be optimized away,
|
||||
// therefore we need to make real interrupt handlers for them.
|
||||
var handlers []llvm.Value
|
||||
handleType := mod.GetTypeByName("runtime/interrupt.handle")
|
||||
if !handleType.IsNil() {
|
||||
handlePtrType := llvm.PointerType(handleType, 0)
|
||||
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||
if global.Type() != handlePtrType {
|
||||
continue
|
||||
}
|
||||
handlers = append(handlers, global)
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over all handler objects, replacing their ptrtoint uses with a
|
||||
// real interrupt ID and creating an interrupt handler for them.
|
||||
for _, global := range handlers {
|
||||
initializer := global.Initializer()
|
||||
num := llvm.ConstExtractValue(initializer, []uint32{1, 0})
|
||||
name := handlerNames[num.SExtValue()]
|
||||
|
||||
if name == "" {
|
||||
errs = append(errs, errorAt(global, fmt.Sprintf("cannot find interrupt name for number %d", num.SExtValue())))
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract the func value.
|
||||
handlerContext := llvm.ConstExtractValue(initializer, []uint32{0, 0})
|
||||
handlerFuncPtr := llvm.ConstExtractValue(initializer, []uint32{0, 1})
|
||||
if !handlerContext.IsConstant() || !handlerFuncPtr.IsConstant() {
|
||||
// This should have been checked already in the compiler.
|
||||
errs = append(errs, errorAt(global, "func value must be constant"))
|
||||
continue
|
||||
}
|
||||
if !handlerFuncPtr.IsAConstantExpr().IsNil() && handlerFuncPtr.Opcode() == llvm.PtrToInt {
|
||||
// This is a ptrtoint: the IR was created for func lowering using a
|
||||
// switch statement.
|
||||
global := handlerFuncPtr.Operand(0)
|
||||
if global.IsAGlobalValue().IsNil() {
|
||||
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(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(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(global, "internal error: unexpected LLVM types in func value"))
|
||||
continue
|
||||
}
|
||||
|
||||
// Check for an existing interrupt handler, and report it as an error if
|
||||
// there is one.
|
||||
fn := mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
fn = llvm.AddFunction(mod, name, fnType)
|
||||
} else if fn.Type().ElementType() != fnType {
|
||||
// Don't bother with a precise error message (listing the previsous
|
||||
// location) because this should not normally happen anyway.
|
||||
errs = append(errs, errorAt(global, name+" redeclared with a different signature"))
|
||||
continue
|
||||
} 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.CalledValue() == handlerFuncPtr && firstInst.Operand(0) == num && firstInst.Operand(1) == handlerContext {
|
||||
// Already defined and apparently identical, so assume this is
|
||||
// fine.
|
||||
continue
|
||||
}
|
||||
|
||||
errValue := name + " redeclared in this program"
|
||||
fnPos := getPosition(fn)
|
||||
if fnPos.IsValid() {
|
||||
errValue += "\n\tprevious declaration at " + fnPos.String()
|
||||
}
|
||||
errs = append(errs, errorAt(global, errValue))
|
||||
continue
|
||||
}
|
||||
|
||||
// Create the wrapper function which is the actual interrupt handler
|
||||
// that is inserted in the interrupt vector.
|
||||
fn.SetUnnamedAddr(true)
|
||||
fn.SetSection(".text." + name)
|
||||
entryBlock := ctx.AddBasicBlock(fn, "entry")
|
||||
builder.SetInsertPointAtEnd(entryBlock)
|
||||
|
||||
// Set the 'interrupt' flag if needed on this platform.
|
||||
if strings.HasPrefix(mod.Target(), "avr") {
|
||||
// This special calling convention is needed on AVR to save and
|
||||
// restore all clobbered registers, instead of just the ones that
|
||||
// would need to be saved/restored in a normal function call.
|
||||
// Note that the AVR_INTERRUPT calling convention would enable
|
||||
// interrupts right at the beginning of the handler, potentially
|
||||
// leading to lots of nested interrupts and a stack overflow.
|
||||
fn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
||||
}
|
||||
|
||||
// Fill the function declaration with the forwarding call.
|
||||
// In practice, the called function will often be inlined which avoids
|
||||
// the extra indirection.
|
||||
builder.CreateCall(handlerFuncPtr, []llvm.Value{num, handlerContext, nullptr}, "")
|
||||
builder.CreateRetVoid()
|
||||
|
||||
// Replace all ptrtoint uses of the global with the interrupt constant.
|
||||
// That can only now be safely done after the interrupt handler has been
|
||||
// created, doing it before the interrupt handler is created might
|
||||
// result in this interrupt handler being optimized away entirely.
|
||||
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)
|
||||
}
|
||||
|
||||
// The runtime/interrput.handle struct can finally be removed.
|
||||
// It would probably be eliminated anyway by a globaldce pass but it's
|
||||
// better to do it now to be sure.
|
||||
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
|
||||
}
|
24
transform/interrupt_test.go
Обычный файл
24
transform/interrupt_test.go
Обычный файл
|
@ -0,0 +1,24 @@
|
|||
package transform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
func TestInterruptLowering(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, subtest := range []string{"avr", "cortexm"} {
|
||||
t.Run(subtest, func(t *testing.T) {
|
||||
testTransform(t, "testdata/interrupt-"+subtest, func(mod llvm.Module) {
|
||||
errs := LowerInterrupts(mod)
|
||||
if len(errs) != 0 {
|
||||
t.Fail()
|
||||
for _, err := range errs {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
33
transform/testdata/interrupt-avr.ll
предоставленный
Обычный файл
33
transform/testdata/interrupt-avr.ll
предоставленный
Обычный файл
|
@ -0,0 +1,33 @@
|
|||
target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"
|
||||
target triple = "avr-atmel-none"
|
||||
|
||||
%"runtime/interrupt.handle" = type { %runtime.funcValue, %"runtime/interrupt.Interrupt" } %runtime.funcValue = type { i8*, i16 }
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i16 }
|
||||
%runtime.funcValueWithSignature = type { i16, %runtime.typecodeID* }
|
||||
%machine.UART = type { %machine.RingBuffer* }
|
||||
%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" }
|
||||
%"runtime/volatile.Register8" = type { i8 }
|
||||
%"runtime/interrupt.Interrupt" = type { i32 }
|
||||
|
||||
@"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" = external constant %runtime.typecodeID
|
||||
@"(machine.UART).Configure$1$withSignature" = internal constant %runtime.funcValueWithSignature { i16 ptrtoint (void (i32, i8*, i8*) addrspace(1)* @"(machine.UART).Configure$1" to i16), %runtime.typecodeID* @"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" }
|
||||
@"runtime/interrupt.$interrupt18" = private unnamed_addr constant %"runtime/interrupt.handle" { %runtime.funcValue { i8* undef, i16 ptrtoint (%runtime.funcValueWithSignature* @"(machine.UART).Configure$1$withSignature" to i16) }, %"runtime/interrupt.Interrupt" { i32 18 } }
|
||||
@machine.UART0 = internal global %machine.UART zeroinitializer
|
||||
@"device/avr.init$string.18" = internal unnamed_addr constant [17 x i8] c"__vector_USART_RX"
|
||||
|
||||
declare void @"(machine.UART).Configure$1"(i32, i8*, i8*) unnamed_addr addrspace(1)
|
||||
|
||||
declare i32 @"runtime/interrupt.Register"(i32, i8*, i16, i8*, i8*) addrspace(1)
|
||||
|
||||
declare void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt") addrspace(1)
|
||||
|
||||
define void @"(machine.UART).Configure"(%machine.RingBuffer*, i32, i8, i8, i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) {
|
||||
call addrspace(1) void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt" { i32 ptrtoint (%"runtime/interrupt.handle"* @"runtime/interrupt.$interrupt18" to i32) })
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @"device/avr.init"(i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) {
|
||||
entry:
|
||||
%0 = call addrspace(1) i32 @"runtime/interrupt.Register"(i32 18, i8* getelementptr inbounds ([17 x i8], [17 x i8]* @"device/avr.init$string.18", i32 0, i32 0), i16 17, i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
35
transform/testdata/interrupt-avr.out.ll
предоставленный
Обычный файл
35
transform/testdata/interrupt-avr.out.ll
предоставленный
Обычный файл
|
@ -0,0 +1,35 @@
|
|||
target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"
|
||||
target triple = "avr-atmel-none"
|
||||
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i16 }
|
||||
%runtime.funcValueWithSignature = type { i16, %runtime.typecodeID* }
|
||||
%machine.UART = type { %machine.RingBuffer* }
|
||||
%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" }
|
||||
%"runtime/volatile.Register8" = type { i8 }
|
||||
%"runtime/interrupt.Interrupt" = type { i32 }
|
||||
|
||||
@"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" = external constant %runtime.typecodeID
|
||||
@"(machine.UART).Configure$1$withSignature" = internal constant %runtime.funcValueWithSignature { i16 ptrtoint (void (i32, i8*, i8*) addrspace(1)* @"(machine.UART).Configure$1" to i16), %runtime.typecodeID* @"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" }
|
||||
@machine.UART0 = internal global %machine.UART zeroinitializer
|
||||
@"device/avr.init$string.18" = internal unnamed_addr constant [17 x i8] c"__vector_USART_RX"
|
||||
|
||||
declare void @"(machine.UART).Configure$1"(i32, i8*, i8*) unnamed_addr addrspace(1)
|
||||
|
||||
declare i32 @"runtime/interrupt.Register"(i32, i8*, i16, i8*, i8*) addrspace(1)
|
||||
|
||||
declare void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt") addrspace(1)
|
||||
|
||||
define void @"(machine.UART).Configure"(%machine.RingBuffer*, i32, i8, i8, i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) {
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @"device/avr.init"(i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define avr_signalcc void @__vector_USART_RX() unnamed_addr addrspace(1) section ".text.__vector_USART_RX" {
|
||||
entry:
|
||||
call addrspace(1) void @"(machine.UART).Configure$1"(i32 18, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
38
transform/testdata/interrupt-cortexm.ll
предоставленный
Обычный файл
38
transform/testdata/interrupt-cortexm.ll
предоставленный
Обычный файл
|
@ -0,0 +1,38 @@
|
|||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv7em-none-eabi"
|
||||
|
||||
%machine.UART = type { %machine.RingBuffer* }
|
||||
%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" }
|
||||
%"runtime/volatile.Register8" = type { i8 }
|
||||
%"runtime/interrupt.handle" = type { { i8*, void (i32, i8*, i8*)* }, %"runtime/interrupt.Interrupt" }
|
||||
%"runtime/interrupt.Interrupt" = type { i32 }
|
||||
|
||||
@"runtime/interrupt.$interrupt2" = private unnamed_addr constant %"runtime/interrupt.handle" { { i8*, void (i32, i8*, i8*)* } { i8* bitcast (%machine.UART* @machine.UART0 to i8*), void (i32, i8*, i8*)* @"(*machine.UART).handleInterrupt$bound" }, %"runtime/interrupt.Interrupt" { i32 2 } }
|
||||
@machine.UART0 = internal global %machine.UART { %machine.RingBuffer* @"machine$alloc.335" }
|
||||
@"machine$alloc.335" = internal global %machine.RingBuffer zeroinitializer
|
||||
@"device/nrf.init$string.2" = internal unnamed_addr constant [23 x i8] c"UARTE0_UART0_IRQHandler"
|
||||
@"device/nrf.init$string.3" = internal unnamed_addr constant [44 x i8] c"SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler"
|
||||
|
||||
declare i32 @"runtime/interrupt.Register"(i32, i8*, i32, i8*, i8*) local_unnamed_addr
|
||||
|
||||
declare void @"device/arm.EnableIRQ"(i32, i8* nocapture readnone, i8* nocapture readnone)
|
||||
|
||||
declare void @"device/arm.SetPriority"(i32, i32, i8* nocapture readnone, i8* nocapture readnone)
|
||||
|
||||
define void @runtime.initAll(i8* nocapture readnone, i8* nocapture readnone) unnamed_addr {
|
||||
entry:
|
||||
%2 = call i32 @"runtime/interrupt.Register"(i32 2, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"device/nrf.init$string.2", i32 0, i32 0), i32 23, i8* undef, i8* undef)
|
||||
%3 = call i32 @"runtime/interrupt.Register"(i32 3, i8* getelementptr inbounds ([44 x i8], [44 x i8]* @"device/nrf.init$string.3", i32 0, i32 0), i32 44, i8* undef, i8* undef)
|
||||
call void @"device/arm.SetPriority"(i32 ptrtoint (%"runtime/interrupt.handle"* @"runtime/interrupt.$interrupt2" to i32), i32 192, i8* undef, i8* undef)
|
||||
call void @"device/arm.EnableIRQ"(i32 ptrtoint (%"runtime/interrupt.handle"* @"runtime/interrupt.$interrupt2" to i32), i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @"(*machine.UART).handleInterrupt$bound"(i32, i8* nocapture %context, i8* nocapture readnone %parentHandle) {
|
||||
entry:
|
||||
%unpack.ptr = bitcast i8* %context to %machine.UART*
|
||||
call void @"(*machine.UART).handleInterrupt"(%machine.UART* %unpack.ptr, i32 %0, i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"(*machine.UART).handleInterrupt"(%machine.UART* nocapture, i32, i8* nocapture readnone, i8* nocapture readnone)
|
39
transform/testdata/interrupt-cortexm.out.ll
предоставленный
Обычный файл
39
transform/testdata/interrupt-cortexm.out.ll
предоставленный
Обычный файл
|
@ -0,0 +1,39 @@
|
|||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv7em-none-eabi"
|
||||
|
||||
%machine.UART = type { %machine.RingBuffer* }
|
||||
%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" }
|
||||
%"runtime/volatile.Register8" = type { i8 }
|
||||
|
||||
@machine.UART0 = internal global %machine.UART { %machine.RingBuffer* @"machine$alloc.335" }
|
||||
@"machine$alloc.335" = internal global %machine.RingBuffer zeroinitializer
|
||||
@"device/nrf.init$string.2" = internal unnamed_addr constant [23 x i8] c"UARTE0_UART0_IRQHandler"
|
||||
@"device/nrf.init$string.3" = internal unnamed_addr constant [44 x i8] c"SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler"
|
||||
|
||||
declare i32 @"runtime/interrupt.Register"(i32, i8*, i32, i8*, i8*) local_unnamed_addr
|
||||
|
||||
declare void @"device/arm.EnableIRQ"(i32, i8* nocapture readnone, i8* nocapture readnone)
|
||||
|
||||
declare void @"device/arm.SetPriority"(i32, i32, i8* nocapture readnone, i8* nocapture readnone)
|
||||
|
||||
define void @runtime.initAll(i8* nocapture readnone, i8* nocapture readnone) unnamed_addr {
|
||||
entry:
|
||||
call void @"device/arm.SetPriority"(i32 2, i32 192, i8* undef, i8* undef)
|
||||
call void @"device/arm.EnableIRQ"(i32 2, i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @"(*machine.UART).handleInterrupt$bound"(i32, i8* nocapture %context, i8* nocapture readnone %parentHandle) {
|
||||
entry:
|
||||
%unpack.ptr = bitcast i8* %context to %machine.UART*
|
||||
call void @"(*machine.UART).handleInterrupt"(%machine.UART* %unpack.ptr, i32 %0, i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"(*machine.UART).handleInterrupt"(%machine.UART* nocapture, i32, i8* nocapture readnone, i8* nocapture readnone)
|
||||
|
||||
define void @UARTE0_UART0_IRQHandler() unnamed_addr section ".text.UARTE0_UART0_IRQHandler" {
|
||||
entry:
|
||||
call void @"(*machine.UART).handleInterrupt$bound"(i32 2, i8* bitcast (%machine.UART* @machine.UART0 to i8*), i8* null)
|
||||
ret void
|
||||
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче