diff --git a/compiler/compiler.go b/compiler/compiler.go index 2a0246971a..f1e1c7568a 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -197,7 +197,7 @@ func (c *Compiler) Compile(mainPath string) []error { }, ShouldOverlay: func(path string) bool { switch path { - case "machine", "os", "reflect", "runtime", "sync": + case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync": return true default: if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") { @@ -696,6 +696,14 @@ func (c *Compiler) parseFunc(frame *Frame) { frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL } + // Some functions have a pragma controlling the inlining level. + switch frame.fn.Inline() { + case ir.InlineHint: + // Add LLVM inline hint to functions with //go:inline pragma. + inline := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0) + frame.fn.LLVMFn.AddFunctionAttr(inline) + } + // Add debug info, if needed. if c.Debug { if frame.fn.Synthetic == "package initializer" { @@ -907,6 +915,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) { case *ssa.Store: llvmAddr := c.getValue(frame, instr.Addr) llvmVal := c.getValue(frame, instr.Val) + c.emitNilCheck(frame, llvmAddr, "store") if c.targetData.TypeAllocSize(llvmVal.Type()) == 0 { // nothing to store return @@ -1117,17 +1126,22 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e // Try to call the function directly for trivially static calls. if fn := instr.StaticCallee(); fn != nil { - switch fn.RelString(nil) { - case "device/arm.ReadRegister": + name := fn.RelString(nil) + switch { + case name == "device/arm.ReadRegister": return c.emitReadRegister(instr.Args) - case "device/arm.Asm", "device/avr.Asm": + case name == "device/arm.Asm" || name == "device/avr.Asm": return c.emitAsm(instr.Args) - case "device/arm.AsmFull", "device/avr.AsmFull": + case name == "device/arm.AsmFull" || name == "device/avr.AsmFull": return c.emitAsmFull(frame, instr) - case "device/arm.SVCall0", "device/arm.SVCall1", "device/arm.SVCall2", "device/arm.SVCall3", "device/arm.SVCall4": + case strings.HasPrefix(name, "device/arm.SVCall"): return c.emitSVCall(frame, instr.Args) - case "syscall.Syscall", "syscall.Syscall6", "syscall.Syscall9": + case strings.HasPrefix(name, "syscall.Syscall"): return c.emitSyscall(frame, instr) + case strings.HasPrefix(name, "runtime/volatile.Load"): + return c.emitVolatileLoad(frame, instr) + case strings.HasPrefix(name, "runtime/volatile.Store"): + return c.emitVolatileStore(frame, instr) } targetFunc := c.ir.GetFunction(fn) diff --git a/compiler/volatile.go b/compiler/volatile.go new file mode 100644 index 0000000000..7ee5b168f1 --- /dev/null +++ b/compiler/volatile.go @@ -0,0 +1,26 @@ +package compiler + +// This file implements volatile loads/stores in runtime/volatile.LoadT and +// runtime/volatile.StoreT as compiler builtins. + +import ( + "golang.org/x/tools/go/ssa" + "tinygo.org/x/go-llvm" +) + +func (c *Compiler) emitVolatileLoad(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { + addr := c.getValue(frame, instr.Args[0]) + c.emitNilCheck(frame, addr, "deref") + val := c.builder.CreateLoad(addr, "") + val.SetVolatile(true) + return val, nil +} + +func (c *Compiler) emitVolatileStore(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { + addr := c.getValue(frame, instr.Args[0]) + val := c.getValue(frame, instr.Args[1]) + c.emitNilCheck(frame, addr, "deref") + store := c.builder.CreateStore(val, addr) + store.SetVolatile(true) + return llvm.Value{}, nil +} diff --git a/ir/ir.go b/ir/ir.go index 3e93e243c9..27a7b0faae 100644 --- a/ir/ir.go +++ b/ir/ir.go @@ -33,11 +33,12 @@ type Program struct { type Function struct { *ssa.Function LLVMFn llvm.Value - 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 + 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 } // Global variable, possibly constant. @@ -69,6 +70,21 @@ type Interface struct { Type *types.Interface } +type InlineType int + +// How much to inline. +const ( + // Default behavior. The compiler decides for itself whether any given + // function will be inlined. Whether any function is inlined depends on the + // optimization level. + InlineDefault InlineType = iota + + // Inline hint, just like the C inline keyword (signalled using + // //go:inline). The compiler will be more likely to inline this function, + // but it is not a guarantee. + InlineHint +) + // Create and initialize a new *Program from a *ssa.Program. func NewProgram(lprogram *loader.Program, mainPath string) *Program { comments := map[string]*ast.CommentGroup{} @@ -279,6 +295,8 @@ func (f *Function) parsePragmas() { } f.linkName = parts[1] f.exported = true + case "//go:inline": + f.inline = InlineHint case "//go:interrupt": if len(parts) != 2 { continue @@ -332,6 +350,11 @@ func (f *Function) IsInterrupt() bool { return f.interrupt } +// Return the inline directive of this function. +func (f *Function) Inline() InlineType { + return f.inline +} + // Return the link name for this function. func (f *Function) LinkName() string { if f.linkName != "" { diff --git a/src/machine/machine_atmega.go b/src/machine/machine_atmega.go index 24fdacf802..4185628e68 100644 --- a/src/machine/machine_atmega.go +++ b/src/machine/machine_atmega.go @@ -10,15 +10,15 @@ import ( func (p GPIO) Configure(config GPIOConfig) { if config.Mode == GPIO_OUTPUT { // set output bit if p.Pin < 8 { - *avr.DDRD |= 1 << p.Pin + avr.DDRD.SetBits(1 << p.Pin) } else { - *avr.DDRB |= 1 << (p.Pin - 8) + avr.DDRB.SetBits(1 << (p.Pin - 8)) } } else { // configure input: clear output bit if p.Pin < 8 { - *avr.DDRD &^= 1 << p.Pin + avr.DDRD.ClearBits(1 << p.Pin) } else { - *avr.DDRB &^= 1 << (p.Pin - 8) + avr.DDRB.ClearBits(1 << (p.Pin - 8)) } } } @@ -26,15 +26,15 @@ func (p GPIO) Configure(config GPIOConfig) { // Get returns the current value of a GPIO pin. func (p GPIO) Get() bool { if p.Pin < 8 { - val := *avr.PIND & (1 << p.Pin) + val := avr.PIND.Get() & (1 << p.Pin) return (val > 0) } else { - val := *avr.PINB & (1 << (p.Pin - 8)) + val := avr.PINB.Get() & (1 << (p.Pin - 8)) return (val > 0) } } -func (p GPIO) getPortMask() (*avr.RegValue, uint8) { +func (p GPIO) getPortMask() (*avr.Register8, uint8) { if p.Pin < 8 { return avr.PORTD, 1 << p.Pin } else { @@ -45,64 +45,64 @@ func (p GPIO) getPortMask() (*avr.RegValue, uint8) { // InitPWM initializes the registers needed for PWM. func InitPWM() { // use waveform generation - *avr.TCCR0A |= avr.TCCR0A_WGM00 + avr.TCCR0A.SetBits(avr.TCCR0A_WGM00) // set timer 0 prescale factor to 64 - *avr.TCCR0B |= avr.TCCR0B_CS01 | avr.TCCR0B_CS00 + avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00) // set timer 1 prescale factor to 64 - *avr.TCCR1B |= avr.TCCR1B_CS11 + avr.TCCR1B.SetBits(avr.TCCR1B_CS11) // put timer 1 in 8-bit phase correct pwm mode - *avr.TCCR1A |= avr.TCCR1A_WGM10 + avr.TCCR1A.SetBits(avr.TCCR1A_WGM10) // set timer 2 prescale factor to 64 - *avr.TCCR2B |= avr.TCCR2B_CS22 + avr.TCCR2B.SetBits(avr.TCCR2B_CS22) // configure timer 2 for phase correct pwm (8-bit) - *avr.TCCR2A |= avr.TCCR2A_WGM20 + avr.TCCR2A.SetBits(avr.TCCR2A_WGM20) } // Configure configures a PWM pin for output. func (pwm PWM) Configure() { if pwm.Pin < 8 { - *avr.DDRD |= 1 << pwm.Pin + avr.DDRD.SetBits(1 << pwm.Pin) } else { - *avr.DDRB |= 1 << (pwm.Pin - 8) + avr.DDRB.SetBits(1 << (pwm.Pin - 8)) } } // Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a // 8-bit value ranging from 0 to 255. func (pwm PWM) Set(value uint16) { - value8 := value >> 8 + value8 := uint8(value >> 8) switch pwm.Pin { case 3: // connect pwm to pin on timer 2, channel B - *avr.TCCR2A |= avr.TCCR2A_COM2B1 - *avr.OCR2B = avr.RegValue(value8) // set pwm duty + avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1) + avr.OCR2B.Set(value8) // set pwm duty case 5: // connect pwm to pin on timer 0, channel B - *avr.TCCR0A |= avr.TCCR0A_COM0B1 - *avr.OCR0B = avr.RegValue(value8) // set pwm duty + avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1) + avr.OCR0B.Set(value8) // set pwm duty case 6: // connect pwm to pin on timer 0, channel A - *avr.TCCR0A |= avr.TCCR0A_COM0A1 - *avr.OCR0A = avr.RegValue(value8) // set pwm duty + avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1) + avr.OCR0A.Set(value8) // set pwm duty case 9: // connect pwm to pin on timer 1, channel A - *avr.TCCR1A |= avr.TCCR1A_COM1A1 + avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1) // this is a 16-bit value, but we only currently allow the low order bits to be set - *avr.OCR1AL = avr.RegValue(value8) // set pwm duty + avr.OCR1AL.Set(value8) // set pwm duty case 10: // connect pwm to pin on timer 1, channel B - *avr.TCCR1A |= avr.TCCR1A_COM1B1 + avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1) // this is a 16-bit value, but we only currently allow the low order bits to be set - *avr.OCR1BL = avr.RegValue(value8) // set pwm duty + avr.OCR1BL.Set(value8) // set pwm duty case 11: // connect pwm to pin on timer 2, channel A - *avr.TCCR2A |= avr.TCCR2A_COM2A1 - *avr.OCR2A = avr.RegValue(value8) // set pwm duty + avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1) + avr.OCR2A.Set(value8) // set pwm duty default: panic("Invalid PWM pin") } @@ -121,19 +121,19 @@ func (i2c I2C) Configure(config I2CConfig) { } // Activate internal pullups for twi. - *avr.PORTC |= (avr.DIDR0_ADC4D | avr.DIDR0_ADC5D) + avr.PORTC.SetBits((avr.DIDR0_ADC4D | avr.DIDR0_ADC5D)) // Initialize twi prescaler and bit rate. - *avr.TWSR |= (avr.TWSR_TWPS0 | avr.TWSR_TWPS1) + avr.TWSR.SetBits((avr.TWSR_TWPS0 | avr.TWSR_TWPS1)) // twi bit rate formula from atmega128 manual pg. 204: // SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR)) // NOTE: TWBR should be 10 or higher for master mode. // It is 72 for a 16mhz board with 100kHz TWI - *avr.TWBR = avr.RegValue(((CPU_FREQUENCY / config.Frequency) - 16) / 2) + avr.TWBR.Set(uint8(((CPU_FREQUENCY / config.Frequency) - 16) / 2)) // Enable twi module. - *avr.TWCR = avr.TWCR_TWEN + avr.TWCR.Set(avr.TWCR_TWEN) } // Tx does a single I2C transaction at the specified address. @@ -162,10 +162,10 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error { // start starts an I2C communication session. func (i2c I2C) start(address uint8, write bool) { // Clear TWI interrupt flag, put start condition on SDA, and enable TWI. - *avr.TWCR = (avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN) + avr.TWCR.Set((avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN)) // Wait till start condition is transmitted. - for (*avr.TWCR & avr.TWCR_TWINT) == 0 { + for (avr.TWCR.Get() & avr.TWCR_TWINT) == 0 { } // Write 7-bit shifted peripheral address. @@ -179,36 +179,36 @@ func (i2c I2C) start(address uint8, write bool) { // stop ends an I2C communication session. func (i2c I2C) stop() { // Send stop condition. - *avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO) + avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO) // Wait for stop condition to be executed on bus. - for (*avr.TWCR & avr.TWCR_TWSTO) == 0 { + for (avr.TWCR.Get() & avr.TWCR_TWSTO) == 0 { } } // writeByte writes a single byte to the I2C bus. func (i2c I2C) writeByte(data byte) { // Write data to register. - *avr.TWDR = avr.RegValue(data) + avr.TWDR.Set(data) // Clear TWI interrupt flag and enable TWI. - *avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT) + avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT) // Wait till data is transmitted. - for (*avr.TWCR & avr.TWCR_TWINT) == 0 { + for (avr.TWCR.Get() & avr.TWCR_TWINT) == 0 { } } // readByte reads a single byte from the I2C bus. func (i2c I2C) readByte() byte { // Clear TWI interrupt flag and enable TWI. - *avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA) + avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA) // Wait till read request is transmitted. - for (*avr.TWCR & avr.TWCR_TWINT) == 0 { + for (avr.TWCR.Get() & avr.TWCR_TWINT) == 0 { } - return byte(*avr.TWDR) + return byte(avr.TWDR.Get()) } // UART on the AVR. @@ -226,32 +226,32 @@ func (uart UART) Configure(config UARTConfig) { // https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html // ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1) ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1) - *avr.UBRR0H = avr.RegValue(ps >> 8) - *avr.UBRR0L = avr.RegValue(ps & 0xff) + avr.UBRR0H.Set(uint8(ps >> 8)) + avr.UBRR0L.Set(uint8(ps & 0xff)) // enable RX, TX and RX interrupt - *avr.UCSR0B = avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0 + avr.UCSR0B.Set(avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0) // 8-bits data - *avr.UCSR0C = avr.UCSR0C_UCSZ01 | avr.UCSR0C_UCSZ00 + avr.UCSR0C.Set(avr.UCSR0C_UCSZ01 | avr.UCSR0C_UCSZ00) } // WriteByte writes a byte of data to the UART. func (uart UART) WriteByte(c byte) error { // Wait until UART buffer is not busy. - for (*avr.UCSR0A & avr.UCSR0A_UDRE0) == 0 { + for (avr.UCSR0A.Get() & avr.UCSR0A_UDRE0) == 0 { } - *avr.UDR0 = avr.RegValue(c) // send char + avr.UDR0.Set(c) // send char return nil } //go:interrupt USART_RX_vect func handleUSART_RX() { // Read register to clear it. - data := *avr.UDR0 + data := avr.UDR0.Get() // Ensure no error. - if (*avr.UCSR0A & (avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0)) == 0 { + if (avr.UCSR0A.Get() & (avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0)) == 0 { // Put data from UDR register into buffer. UART0.Receive(byte(data)) } diff --git a/src/machine/machine_attiny.go b/src/machine/machine_attiny.go index 454d4a299a..3a7354bf03 100644 --- a/src/machine/machine_attiny.go +++ b/src/machine/machine_attiny.go @@ -9,19 +9,19 @@ import ( // Configure sets the pin to input or output. func (p GPIO) Configure(config GPIOConfig) { if config.Mode == GPIO_OUTPUT { // set output bit - *avr.DDRB |= 1 << p.Pin + avr.DDRB.SetBits(1 << p.Pin) } else { // configure input: clear output bit - *avr.DDRB &^= 1 << p.Pin + avr.DDRB.ClearBits(1 << p.Pin) } } -func (p GPIO) getPortMask() (*avr.RegValue, uint8) { +func (p GPIO) getPortMask() (*avr.Register8, uint8) { return avr.PORTB, 1 << p.Pin } // Get returns the current value of a GPIO pin. func (p GPIO) Get() bool { - val := *avr.PINB & (1 << p.Pin) + val := avr.PINB.Get() & (1 << p.Pin) return (val > 0) } diff --git a/src/machine/machine_avr.go b/src/machine/machine_avr.go index 6331960c8f..ffb88d913a 100644 --- a/src/machine/machine_avr.go +++ b/src/machine/machine_avr.go @@ -17,10 +17,10 @@ const ( func (p GPIO) Set(value bool) { if value { // set bits port, mask := p.PortMaskSet() - *port = mask + port.Set(mask) } else { // clear bits port, mask := p.PortMaskClear() - *port = mask + port.Set(mask) } } @@ -30,9 +30,9 @@ func (p GPIO) Set(value bool) { // Warning: there are no separate pin set/clear registers on the AVR. The // returned mask is only valid as long as no other pin in the same port has been // changed. -func (p GPIO) PortMaskSet() (*avr.RegValue, avr.RegValue) { +func (p GPIO) PortMaskSet() (*avr.Register8, uint8) { port, mask := p.getPortMask() - return port, *port | avr.RegValue(mask) + return port, port.Get() | mask } // Return the register and mask to disable a given port. This can be used to @@ -41,18 +41,18 @@ func (p GPIO) PortMaskSet() (*avr.RegValue, avr.RegValue) { // Warning: there are no separate pin set/clear registers on the AVR. The // returned mask is only valid as long as no other pin in the same port has been // changed. -func (p GPIO) PortMaskClear() (*avr.RegValue, avr.RegValue) { +func (p GPIO) PortMaskClear() (*avr.Register8, uint8) { port, mask := p.getPortMask() - return port, *port &^ avr.RegValue(mask) + return port, port.Get() &^ mask } // InitADC initializes the registers needed for ADC. func InitADC() { // set a2d prescaler so we are inside the desired 50-200 KHz range at 16MHz. - *avr.ADCSRA |= (avr.ADCSRA_ADPS2 | avr.ADCSRA_ADPS1 | avr.ADCSRA_ADPS0) + avr.ADCSRA.SetBits(avr.ADCSRA_ADPS2 | avr.ADCSRA_ADPS1 | avr.ADCSRA_ADPS0) // enable a2d conversions - *avr.ADCSRA |= avr.ADCSRA_ADEN + avr.ADCSRA.SetBits(avr.ADCSRA_ADEN) } // Configure configures a ADCPin to be able to be used to read data. @@ -68,18 +68,16 @@ func (a ADC) Get() uint16 { // set the ADLAR bit (left-adjusted result) to get a value scaled to 16 // bits. This has the same effect as shifting the return value left by 6 // bits. - *avr.ADMUX = avr.RegValue(avr.ADMUX_REFS0 | avr.ADMUX_ADLAR | (a.Pin & 0x07)) + avr.ADMUX.Set(avr.ADMUX_REFS0 | avr.ADMUX_ADLAR | (a.Pin & 0x07)) // start the conversion - *avr.ADCSRA |= avr.ADCSRA_ADSC + avr.ADCSRA.SetBits(avr.ADCSRA_ADSC) // ADSC is cleared when the conversion finishes - for ok := true; ok; ok = (*avr.ADCSRA & avr.ADCSRA_ADSC) > 0 { + for ok := true; ok; ok = (avr.ADCSRA.Get() & avr.ADCSRA_ADSC) > 0 { } - low := uint16(*avr.ADCL) - high := uint16(*avr.ADCH) - return uint16(low) | uint16(high<<8) + return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8 } // I2C on AVR. diff --git a/src/runtime/runtime_atmega.go b/src/runtime/runtime_atmega.go index 474f34c400..9e1e6cbf96 100644 --- a/src/runtime/runtime_atmega.go +++ b/src/runtime/runtime_atmega.go @@ -17,19 +17,19 @@ func sleepWDT(period uint8) { avr.Asm("cli") avr.Asm("wdr") // Start timed sequence. - *avr.WDTCSR |= avr.WDTCSR_WDCE | avr.WDTCSR_WDE + avr.WDTCSR.SetBits(avr.WDTCSR_WDCE | avr.WDTCSR_WDE) // Enable WDT and set new timeout - *avr.WDTCSR = avr.WDTCSR_WDIE | avr.RegValue(period) + avr.WDTCSR.SetBits(avr.WDTCSR_WDIE | period) avr.Asm("sei") // Set sleep mode to idle and enable sleep mode. // Note: when using something other than idle, the UART won't work // correctly. This needs to be fixed, though, so we can truly sleep. - *avr.SMCR = (0 << 1) | avr.SMCR_SE + avr.SMCR.Set((0 << 1) | avr.SMCR_SE) // go to sleep avr.Asm("sleep") // disable sleep - *avr.SMCR = 0 + avr.SMCR.Set(0) } diff --git a/src/runtime/volatile/volatile.go b/src/runtime/volatile/volatile.go new file mode 100644 index 0000000000..47262f3470 --- /dev/null +++ b/src/runtime/volatile/volatile.go @@ -0,0 +1,34 @@ +// Package volatile provides definitions for volatile loads and stores. These +// are implemented as compiler builtins. +// +// The load operations load a volatile value. The store operations store to a +// volatile value. The compiler will emit exactly one load or store operation +// when possible and will not reorder volatile operations. However, the compiler +// may move other operations across load/store operations, so make sure that all +// relevant loads/stores are done in a volatile way if this is a problem. +// +// These loads and stores are commonly used to read/write values from memory +// mapped peripheral devices. They do not provide atomicity, use the sync/atomic +// package for that. +// +// For more details: https://llvm.org/docs/LangRef.html#volatile-memory-accesses +// and https://blog.regehr.org/archives/28. +package volatile + +// LoadUint8 loads the volatile value *addr. +func LoadUint8(addr *uint8) (val uint8) + +// LoadUint16 loads the volatile value *addr. +func LoadUint16(addr *uint16) (val uint16) + +// LoadUint32 loads the volatile value *addr. +func LoadUint32(addr *uint32) (val uint32) + +// StoreUint8 stores val to the volatile value *addr. +func StoreUint8(addr *uint8, val uint8) + +// StoreUint16 stores val to the volatile value *addr. +func StoreUint16(addr *uint16, val uint16) + +// StoreUint32 stores val to the volatile value *addr. +func StoreUint32(addr *uint32, val uint32) diff --git a/tools/gen-device-avr.py b/tools/gen-device-avr.py index f94644a9cf..f42691cc7e 100755 --- a/tools/gen-device-avr.py +++ b/tools/gen-device-avr.py @@ -151,12 +151,54 @@ def writeGo(outdir, device): // {description} package {pkgName} -import "unsafe" +import ( + "runtime/volatile" + "unsafe" +) // Special type that causes loads/stores to be volatile (necessary for // memory-mapped registers). -//go:volatile -type RegValue uint8 +type Register8 struct {{ + Reg uint8 +}} + +// Get returns the value in the register. It is the volatile equivalent of: +// +// *r.Reg +// +//go:inline +func (r *Register8) Get() uint8 {{ + return volatile.LoadUint8(&r.Reg) +}} + +// Set updates the register value. It is the volatile equivalent of: +// +// *r.Reg = value +// +//go:inline +func (r *Register8) Set(value uint8) {{ + volatile.StoreUint8(&r.Reg, value) +}} + +// SetBits reads the register, sets the given bits, and writes it back. It is +// the volatile equivalent of: +// +// r.Reg |= value +// +//go:inline +func (r *Register8) SetBits(value uint8) {{ + volatile.StoreUint8(&r.Reg, volatile.LoadUint8(&r.Reg) | value) +}} + +// ClearBits reads the register, clears the given bits, and writes it back. It +// is the volatile equivalent of: +// +// r.Reg &^= value +// +//go:inline +func (r *Register8) ClearBits(value uint8) {{ + volatile.StoreUint8(&r.Reg, volatile.LoadUint8(&r.Reg) &^ value) +}} // Some information about this device. const ( @@ -179,7 +221,7 @@ def writeGo(outdir, device): out.write('\n\t// {description}\n'.format(**peripheral)) for register in peripheral['registers']: for variant in register['variants']: - out.write('\t{name} = (*RegValue)(unsafe.Pointer(uintptr(0x{address:x})))\n'.format(**variant)) + out.write('\t{name} = (*Register8)(unsafe.Pointer(uintptr(0x{address:x})))\n'.format(**variant)) out.write(')\n') for peripheral in device.peripherals: