diff options
author | Ayke van Laethem <[email protected]> | 2018-11-20 18:34:14 +0100 |
---|---|---|
committer | Ayke van Laethem <[email protected]> | 2018-11-20 18:50:24 +0100 |
commit | 9392ef900d3709108f054f2ef48c79435a1f4efe (patch) | |
tree | 9870904b128c4fabf0f79f33e526f568da3d2a23 | |
parent | a96e2879b2a2d7fdb783e50b043424df41c5637e (diff) | |
download | tinygo-9392ef900d3709108f054f2ef48c79435a1f4efe.tar.gz tinygo-9392ef900d3709108f054f2ef48c79435a1f4efe.zip |
avr: add support for the digispark
Blinking the on-board LED works. Nothing else has been tested yet.
-rw-r--r-- | .travis.yml | 3 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | docs/targets.rst | 3 | ||||
-rw-r--r-- | src/machine/board_digispark.go | 7 | ||||
-rw-r--r-- | src/machine/machine_atmega.go | 262 | ||||
-rw-r--r-- | src/machine/machine_attiny.go | 48 | ||||
-rw-r--r-- | src/machine/machine_avr.go | 257 | ||||
-rw-r--r-- | src/runtime/runtime_atmega.go | 35 | ||||
-rw-r--r-- | src/runtime/runtime_attiny.go | 16 | ||||
-rw-r--r-- | src/runtime/runtime_avr.go | 28 | ||||
-rw-r--r-- | targets/arduino.json | 5 | ||||
-rw-r--r-- | targets/avr.json | 5 | ||||
-rw-r--r-- | targets/digispark.json | 17 | ||||
-rwxr-xr-x | tools/gen-device-avr.py | 25 |
14 files changed, 421 insertions, 291 deletions
diff --git a/.travis.yml b/.travis.yml index 7a684902c..4ede432f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,4 +25,5 @@ script: - tinygo build -o blinky1.nrf51.elf -target=microbit examples/echo - tinygo build -o test.nrf.elf -target=nrf52840-mdk examples/blinky1 - tinygo build -o blinky1.stm32.elf -target=bluepill examples/blinky1 - - tinygo build -o blinky1.avr.o -target=arduino examples/blinky1 # TODO: avr-as/avr-gcc doesn't work + - tinygo build -o blinky1.o -target=arduino examples/blinky1 # TODO: avr-as/avr-gcc doesn't work + - tinygo build -o blinky1.o -target=digispark examples/blinky1 @@ -72,6 +72,7 @@ gen-device: gen-device-avr gen-device-nrf gen-device-stm32 gen-device-avr: ./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/ + ./tools/gen-device-avr.py lib/avr/packs/tiny src/device/avr/ go fmt ./src/device/avr gen-device-nrf: diff --git a/docs/targets.rst b/docs/targets.rst index 6f7c6d96a..741dc3ec1 100644 --- a/docs/targets.rst +++ b/docs/targets.rst @@ -47,6 +47,9 @@ Note: the AVR backend of LLVM is still experimental so you may encounter bugs. * `Arduino Uno <https://store.arduino.cc/arduino-uno-rev3>`_ (`ATmega328p <https://www.microchip.com/wwwproducts/en/ATmega328p>`_) + * `Digispark <http://digistump.com/products/1>`_ (`ATtiny85 + <https://www.microchip.com/wwwproducts/en/ATtiny85>`_) |br| + Very limited support at the moment. WebAssembly diff --git a/src/machine/board_digispark.go b/src/machine/board_digispark.go new file mode 100644 index 000000000..e4065d357 --- /dev/null +++ b/src/machine/board_digispark.go @@ -0,0 +1,7 @@ +// +build attiny85,digispark + +package machine + +const ( + LED = 1 +) diff --git a/src/machine/machine_atmega.go b/src/machine/machine_atmega.go new file mode 100644 index 000000000..ed93fc3eb --- /dev/null +++ b/src/machine/machine_atmega.go @@ -0,0 +1,262 @@ +// +build avr,atmega + +package machine + +import ( + "device/avr" +) + +// Configure sets the pin to input or output. +func (p GPIO) Configure(config GPIOConfig) { + if config.Mode == GPIO_OUTPUT { // set output bit + if p.Pin < 8 { + *avr.DDRD |= 1 << p.Pin + } else { + *avr.DDRB |= 1 << (p.Pin - 8) + } + } else { // configure input: clear output bit + if p.Pin < 8 { + *avr.DDRD &^= 1 << p.Pin + } else { + *avr.DDRB &^= 1 << (p.Pin - 8) + } + } +} + +// Set changes the value of the GPIO pin. The pin must be configured as output. +func (p GPIO) Set(value bool) { + if value { // set bits + if p.Pin < 8 { + *avr.PORTD |= 1 << p.Pin + } else { + *avr.PORTB |= 1 << (p.Pin - 8) + } + } else { // clear bits + if p.Pin < 8 { + *avr.PORTD &^= 1 << p.Pin + } else { + *avr.PORTB &^= 1 << (p.Pin - 8) + } + } +} + +// Get returns the current value of a GPIO pin. +func (p GPIO) Get() bool { + if p.Pin < 8 { + val := *avr.PIND & (1 << p.Pin) + return (val > 0) + } else { + val := *avr.PINB & (1 << (p.Pin - 8)) + return (val > 0) + } +} + +// InitPWM initializes the registers needed for PWM. +func InitPWM() { + // use waveform generation + *avr.TCCR0A |= avr.TCCR0A_WGM00 + + // set timer 0 prescale factor to 64 + *avr.TCCR0B |= avr.TCCR0B_CS01 | avr.TCCR0B_CS00 + + // set timer 1 prescale factor to 64 + *avr.TCCR1B |= avr.TCCR1B_CS11 + + // put timer 1 in 8-bit phase correct pwm mode + *avr.TCCR1A |= avr.TCCR1A_WGM10 + + // set timer 2 prescale factor to 64 + *avr.TCCR2B |= avr.TCCR2B_CS22 + + // configure timer 2 for phase correct pwm (8-bit) + *avr.TCCR2A |= avr.TCCR2A_WGM20 +} + +// Configure configures a PWM pin for output. +func (pwm PWM) Configure() { + if pwm.Pin < 8 { + *avr.DDRD |= 1 << pwm.Pin + } else { + *avr.DDRB |= 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 + 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 + case 5: + // connect pwm to pin on timer 0, channel B + *avr.TCCR0A |= avr.TCCR0A_COM0B1 + *avr.OCR0B = avr.RegValue(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 + case 9: + // connect pwm to pin on timer 1, channel A + *avr.TCCR1A |= 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 + case 10: + // connect pwm to pin on timer 1, channel B + *avr.TCCR1A |= 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 + case 11: + // connect pwm to pin on timer 2, channel A + *avr.TCCR2A |= avr.TCCR2A_COM2A1 + *avr.OCR2A = avr.RegValue(value8) // set pwm duty + default: + panic("Invalid PWM pin") + } +} + +// I2CConfig is used to store config info for I2C. +type I2CConfig struct { + Frequency uint32 +} + +// Configure is intended to setup the I2C interface. +func (i2c I2C) Configure(config I2CConfig) { + // Default I2C bus speed is 100 kHz. + if config.Frequency == 0 { + config.Frequency = TWI_FREQ_100KHZ + } + + // Activate internal pullups for twi. + *avr.PORTC |= (avr.DIDR0_ADC4D | avr.DIDR0_ADC5D) + + // Initialize twi prescaler and bit rate. + *avr.TWSR |= (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) + + // Enable twi module. + *avr.TWCR = avr.TWCR_TWEN +} + +// Tx does a single I2C transaction at the specified address. +// It clocks out the given address, writes the bytes in w, reads back len(r) +// bytes and stores them in r, and generates a stop condition on the bus. +func (i2c I2C) Tx(addr uint16, w, r []byte) error { + if len(w) != 0 { + i2c.start(uint8(addr), true) // start transmission for writing + for _, b := range w { + i2c.writeByte(b) + } + } + if len(r) != 0 { + i2c.start(uint8(addr), false) // re-start transmission for reading + for i := range r { // read each char + r[i] = i2c.readByte() + } + } + if len(w) != 0 || len(r) != 0 { + // Stop the transmission after it has been started. + i2c.stop() + } + return nil +} + +// 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) + + // Wait till start condition is transmitted. + for (*avr.TWCR & avr.TWCR_TWINT) == 0 { + } + + // Write 7-bit shifted peripheral address. + address <<= 1 + if !write { + address |= 1 // set read flag + } + i2c.writeByte(address) +} + +// stop ends an I2C communication session. +func (i2c I2C) stop() { + // Send stop condition. + *avr.TWCR = (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 { + } +} + +// writeByte writes a single byte to the I2C bus. +func (i2c I2C) writeByte(data byte) { + // Write data to register. + *avr.TWDR = avr.RegValue(data) + + // Clear TWI interrupt flag and enable TWI. + *avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT) + + // Wait till data is transmitted. + for (*avr.TWCR & 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) + + // Wait till read request is transmitted. + for (*avr.TWCR & avr.TWCR_TWINT) == 0 { + } + + return byte(*avr.TWDR) +} + +// Configure the UART on the AVR. Defaults to 9600 baud on Arduino. +func (uart UART) Configure(config UARTConfig) { + if config.BaudRate == 0 { + config.BaudRate = 9600 + } + + // 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) + ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1) + *avr.UBRR0H = avr.RegValue(ps >> 8) + *avr.UBRR0L = avr.RegValue(ps & 0xff) + + // enable RX, TX and RX interrupt + *avr.UCSR0B = avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0 + + // 8-bits data + *avr.UCSR0C = 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 { + } + *avr.UDR0 = avr.RegValue(c) // send char + return nil +} + +//go:interrupt USART_RX_vect +func handleUSART_RX() { + // Read register to clear it. + data := *avr.UDR0 + + // Ensure no error. + if (*avr.UCSR0A & (avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0)) == 0 { + // Put data from UDR register into buffer. + bufferPut(byte(data)) + } +} diff --git a/src/machine/machine_attiny.go b/src/machine/machine_attiny.go new file mode 100644 index 000000000..eadcee2a5 --- /dev/null +++ b/src/machine/machine_attiny.go @@ -0,0 +1,48 @@ +// +build avr,attiny + +package machine + +import ( + "device/avr" +) + +// 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 + } else { // configure input: clear output bit + *avr.DDRB &^= 1 << p.Pin + } +} + +// Set changes the value of the GPIO pin. The pin must be configured as output. +func (p GPIO) Set(value bool) { + if value { // set bits + *avr.PORTB |= 1 << p.Pin + } else { // clear bits + *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) + return (val > 0) +} + +// Configure is a dummy implementation. UART has not been implemented for ATtiny +// devices. +func (uart UART) Configure(config UARTConfig) { +} + +// WriteByte is a dummy implementation. UART has not been implemented for ATtiny +// devices. +func (uart UART) WriteByte(c byte) error { + return nil +} + +// Tx is a dummy implementation. I2C has not been implemented for ATtiny +// devices. +func (i2c I2C) Tx(addr uint16, w, r []byte) error { + return nil +} diff --git a/src/machine/machine_avr.go b/src/machine/machine_avr.go index dcced7574..5a6c4bab5 100644 --- a/src/machine/machine_avr.go +++ b/src/machine/machine_avr.go @@ -13,115 +13,6 @@ const ( GPIO_OUTPUT ) -func (p GPIO) Configure(config GPIOConfig) { - if config.Mode == GPIO_OUTPUT { // set output bit - if p.Pin < 8 { - *avr.DDRD |= 1 << p.Pin - } else { - *avr.DDRB |= 1 << (p.Pin - 8) - } - } else { // configure input: clear output bit - if p.Pin < 8 { - *avr.DDRD &^= 1 << p.Pin - } else { - *avr.DDRB &^= 1 << (p.Pin - 8) - } - } -} - -func (p GPIO) Set(value bool) { - if value { // set bits - if p.Pin < 8 { - *avr.PORTD |= 1 << p.Pin - } else { - *avr.PORTB |= 1 << (p.Pin - 8) - } - } else { // clear bits - if p.Pin < 8 { - *avr.PORTD &^= 1 << p.Pin - } else { - *avr.PORTB &^= 1 << (p.Pin - 8) - } - } -} - -// Get returns the current value of a GPIO pin. -func (p GPIO) Get() bool { - if p.Pin < 8 { - val := *avr.PIND & (1 << p.Pin) - return (val > 0) - } else { - val := *avr.PINB & (1 << (p.Pin - 8)) - return (val > 0) - } -} - -// InitPWM initializes the registers needed for PWM. -func InitPWM() { - // use waveform generation - *avr.TCCR0A |= avr.TCCR0A_WGM00 - - // set timer 0 prescale factor to 64 - *avr.TCCR0B |= avr.TCCR0B_CS01 | avr.TCCR0B_CS00 - - // set timer 1 prescale factor to 64 - *avr.TCCR1B |= avr.TCCR1B_CS11 - - // put timer 1 in 8-bit phase correct pwm mode - *avr.TCCR1A |= avr.TCCR1A_WGM10 - - // set timer 2 prescale factor to 64 - *avr.TCCR2B |= avr.TCCR2B_CS22 - - // configure timer 2 for phase correct pwm (8-bit) - *avr.TCCR2A |= avr.TCCR2A_WGM20 -} - -// Configure configures a PWM pin for output. -func (pwm PWM) Configure() { - if pwm.Pin < 8 { - *avr.DDRD |= 1 << pwm.Pin - } else { - *avr.DDRB |= 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 - 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 - case 5: - // connect pwm to pin on timer 0, channel B - *avr.TCCR0A |= avr.TCCR0A_COM0B1 - *avr.OCR0B = avr.RegValue(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 - case 9: - // connect pwm to pin on timer 1, channel A - *avr.TCCR1A |= 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 - case 10: - // connect pwm to pin on timer 1, channel B - *avr.TCCR1A |= 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 - case 11: - // connect pwm to pin on timer 2, channel A - *avr.TCCR2A |= avr.TCCR2A_COM2A1 - *avr.OCR2A = avr.RegValue(value8) // set pwm duty - default: - panic("Invalid PWM pin") - } -} - // InitADC initializes the registers needed for ADC. func InitADC() { // set a2d prescaler so we are inside the desired 50-200 KHz range at 16MHz. @@ -158,159 +49,15 @@ func (a ADC) Get() uint16 { return uint16(low) | uint16(high<<8) } -// I2C on the Arduino. +// I2C on AVR. type I2C struct { } -// I2C0 is the only I2C interface on the Arduino. +// I2C0 is the only I2C interface on most AVRs. var I2C0 = I2C{} -// I2CConfig is used to store config info for I2C. -type I2CConfig struct { - Frequency uint32 -} - -// Configure is intended to setup the I2C interface. -func (i2c I2C) Configure(config I2CConfig) { - // Default I2C bus speed is 100 kHz. - if config.Frequency == 0 { - config.Frequency = TWI_FREQ_100KHZ - } - - // Activate internal pullups for twi. - *avr.PORTC |= (avr.DIDR0_ADC4D | avr.DIDR0_ADC5D) - - // Initialize twi prescaler and bit rate. - *avr.TWSR |= (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) - - // Enable twi module. - *avr.TWCR = avr.TWCR_TWEN -} - -// Tx does a single I2C transaction at the specified address. -// It clocks out the given address, writes the bytes in w, reads back len(r) -// bytes and stores them in r, and generates a stop condition on the bus. -func (i2c I2C) Tx(addr uint16, w, r []byte) error { - if len(w) != 0 { - i2c.start(uint8(addr), true) // start transmission for writing - for _, b := range w { - i2c.writeByte(b) - } - } - if len(r) != 0 { - i2c.start(uint8(addr), false) // re-start transmission for reading - for i := range r { // read each char - r[i] = i2c.readByte() - } - } - if len(w) != 0 || len(r) != 0 { - // Stop the transmission after it has been started. - i2c.stop() - } - return nil -} - -// 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) - - // Wait till start condition is transmitted. - for (*avr.TWCR & avr.TWCR_TWINT) == 0 { - } - - // Write 7-bit shifted peripheral address. - address <<= 1 - if !write { - address |= 1 // set read flag - } - i2c.writeByte(address) -} - -// stop ends an I2C communication session. -func (i2c I2C) stop() { - // Send stop condition. - *avr.TWCR = (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 { - } -} - -// writeByte writes a single byte to the I2C bus. -func (i2c I2C) writeByte(data byte) { - // Write data to register. - *avr.TWDR = avr.RegValue(data) - - // Clear TWI interrupt flag and enable TWI. - *avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT) - - // Wait till data is transmitted. - for (*avr.TWCR & 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) - - // Wait till read request is transmitted. - for (*avr.TWCR & avr.TWCR_TWINT) == 0 { - } - - return byte(*avr.TWDR) -} - // UART var ( // UART0 is the hardware serial port on the AVR. UART0 = &UART{} ) - -// Configure the UART on the AVR. Defaults to 9600 baud on Arduino. -func (uart UART) Configure(config UARTConfig) { - if config.BaudRate == 0 { - config.BaudRate = 9600 - } - - // 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) - ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1) - *avr.UBRR0H = avr.RegValue(ps >> 8) - *avr.UBRR0L = avr.RegValue(ps & 0xff) - - // enable RX, TX and RX interrupt - *avr.UCSR0B = avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0 - - // 8-bits data - *avr.UCSR0C = 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 { - } - *avr.UDR0 = avr.RegValue(c) // send char - return nil -} - -//go:interrupt USART_RX_vect -func handleUSART_RX() { - // Read register to clear it. - data := *avr.UDR0 - - // Ensure no error. - if (*avr.UCSR0A & (avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0)) == 0 { - // Put data from UDR register into buffer. - bufferPut(byte(data)) - } -} diff --git a/src/runtime/runtime_atmega.go b/src/runtime/runtime_atmega.go new file mode 100644 index 000000000..474f34c40 --- /dev/null +++ b/src/runtime/runtime_atmega.go @@ -0,0 +1,35 @@ +// +build avr,atmega + +package runtime + +import ( + "device/avr" +) + +// Sleep for a given period. The period is defined by the WDT peripheral, and is +// on most chips (at least) 3 bits wide, in powers of two from 16ms to 2s +// (0=16ms, 1=32ms, 2=64ms...). Note that the WDT is not very accurate: it can +// be off by a large margin depending on temperature and supply voltage. +// +// TODO: disable more peripherals etc. to reduce sleep current. +func sleepWDT(period uint8) { + // Configure WDT + avr.Asm("cli") + avr.Asm("wdr") + // Start timed sequence. + *avr.WDTCSR |= avr.WDTCSR_WDCE | avr.WDTCSR_WDE + // Enable WDT and set new timeout + *avr.WDTCSR = avr.WDTCSR_WDIE | avr.RegValue(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 + + // go to sleep + avr.Asm("sleep") + + // disable sleep + *avr.SMCR = 0 +} diff --git a/src/runtime/runtime_attiny.go b/src/runtime/runtime_attiny.go new file mode 100644 index 000000000..e0f6cc2eb --- /dev/null +++ b/src/runtime/runtime_attiny.go @@ -0,0 +1,16 @@ +// +build avr,attiny + +package runtime + +import ( + "device/avr" +) + +func sleepWDT(period uint8) { + // TODO: use the watchdog timer instead of a busy loop. + for i := 0x45; i != 0; i-- { + for i := 0xff; i != 0; i-- { + avr.Asm("nop") + } + } +} diff --git a/src/runtime/runtime_avr.go b/src/runtime/runtime_avr.go index e01d2dce1..deeaa4ed1 100644 --- a/src/runtime/runtime_avr.go +++ b/src/runtime/runtime_avr.go @@ -83,34 +83,6 @@ func sleepTicks(d timeUnit) { } } -// Sleep for a given period. The period is defined by the WDT peripheral, and is -// on most chips (at least) 3 bits wide, in powers of two from 16ms to 2s -// (0=16ms, 1=32ms, 2=64ms...). Note that the WDT is not very accurate: it can -// be off by a large margin depending on temperature and supply voltage. -// -// TODO: disable more peripherals etc. to reduce sleep current. -func sleepWDT(period uint8) { - // Configure WDT - avr.Asm("cli") - avr.Asm("wdr") - // Start timed sequence. - *avr.WDTCSR |= avr.WDTCSR_WDCE | avr.WDTCSR_WDE - // Enable WDT and set new timeout (0.5s) - *avr.WDTCSR = avr.WDTCSR_WDIE | avr.RegValue(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 - - // go to sleep - avr.Asm("sleep") - - // disable sleep - *avr.SMCR = 0 -} - func ticks() timeUnit { return currentTime } diff --git a/targets/arduino.json b/targets/arduino.json index be7c52601..795a99564 100644 --- a/targets/arduino.json +++ b/targets/arduino.json @@ -1,7 +1,7 @@ { + "inherits": ["avr"], "llvm-target": "avr-atmel-none", - "build-tags": ["arduino", "atmega328p", "atmega", "avr5", "avr", "js", "wasm"], - "linker": "avr-gcc", + "build-tags": ["arduino", "atmega328p", "atmega", "avr5"], "pre-link-args": [ "-nostartfiles", "-mmcu=avr5", @@ -13,6 +13,5 @@ "targets/avr.S", "src/device/avr/atmega328p.s" ], - "objcopy": "avr-objcopy", "flash": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}" } diff --git a/targets/avr.json b/targets/avr.json new file mode 100644 index 000000000..debb54786 --- /dev/null +++ b/targets/avr.json @@ -0,0 +1,5 @@ +{ + "build-tags": ["avr", "js", "wasm"], + "linker": "avr-gcc", + "objcopy": "avr-objcopy" +} diff --git a/targets/digispark.json b/targets/digispark.json new file mode 100644 index 000000000..02d9b87e4 --- /dev/null +++ b/targets/digispark.json @@ -0,0 +1,17 @@ +{ + "inherits": ["avr"], + "llvm-target": "avr-atmel-none", + "build-tags": ["digispark", "attiny85", "attiny", "avr2", "avr25"], + "pre-link-args": [ + "-nostartfiles", + "-mmcu=attiny85", + "-Wl,--defsym=_bootloader_size=2180", + "-Wl,--defsym=_stack_size=128", + "-T", "src/device/avr/attiny85.ld", + "-T", "targets/avr.ld", + "-Wl,--gc-sections", + "targets/avr.S", + "src/device/avr/attiny85.s" + ], + "flash": "micronucleus --run {hex}" +} diff --git a/tools/gen-device-avr.py b/tools/gen-device-avr.py index 1b7efe3fd..00cf7ba1f 100755 --- a/tools/gen-device-avr.py +++ b/tools/gen-device-avr.py @@ -93,10 +93,16 @@ def readATDF(path): continue for bitfieldEl in regEl.getElementsByTagName('bitfield'): + mask = bitfieldEl.getAttribute('mask') + if len(mask) == 2: + # Two devices (ATtiny102 and ATtiny104) appear to have + # an error in the bitfields, leaving out the '0x' + # prefix. + mask = '0x' + mask reg['bitfields'].append({ 'name': regName + '_' + bitfieldEl.getAttribute('name'), 'description': bitfieldEl.getAttribute('caption'), - 'value': int(bitfieldEl.getAttribute('mask'), 0), + 'value': int(mask, 0), }) if regName in allRegisters: @@ -112,6 +118,11 @@ def readATDF(path): peripheral['registers'].append(reg) + ramSize = 0 # for devices with no RAM + for ramSegmentName in ['IRAM', 'INTERNAL_SRAM', 'SRAM']: + if ramSegmentName in memorySizes['data']['segments']: + ramSize = memorySizes['data']['segments'][ramSegmentName] + device.metadata = { 'file': os.path.basename(path), 'descriptorSource': 'http://packs.download.atmel.com/', @@ -121,7 +132,7 @@ def readATDF(path): 'arch': arch, 'family': family, 'flashSize': memorySizes['prog']['size'], - 'ramSize': memorySizes['data']['segments'].get('IRAM', memorySizes['data']['segments'].get('INTERNAL_SRAM')), + 'ramSize': ramSize, 'numInterrupts': len(device.interrupts), } @@ -228,15 +239,21 @@ __vector_default: '''.format(**device.metadata)) num = 0 for intr in device.interrupts: + jmp = 'jmp' + if device.metadata['flashSize'] <= 8 * 1024: + # When a device has 8kB or less flash, rjmp (2 bytes) must be used + # instead of jmp (4 bytes). + # https://www.avrfreaks.net/forum/rjmp-versus-jmp + jmp = 'rjmp' if intr['index'] < num: # Some devices have duplicate interrupts, probably for historical # reasons. continue while intr['index'] > num: - out.write(' jmp __vector_default\n') + out.write(' {jmp} __vector_default\n'.format(jmp=jmp)) num += 1 num += 1 - out.write(' jmp __vector_{name}\n'.format(**intr)) + out.write(' {jmp} __vector_{name}\n'.format(jmp=jmp, **intr)) out.write(''' ; Define default implementations for interrupts, redirecting to |