Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ ifneq ($(AVR), 0)
endif
ifneq ($(XTENSA), 0)
$(TINYGO) build -size short -o test.bin -target=esp32-wroom-32 examples/blinky1
@$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
@$(MD5SUM) test.bin
endif
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
@$(MD5SUM) test.hex
Expand Down
7 changes: 4 additions & 3 deletions builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,11 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil {
return err
}
case "esp32":
// Special format for the ESP32 chip (parsed by the ROM bootloader).
case "esp32", "esp8266":
// Special format for the ESP family of chips (parsed by the ROM
// bootloader).
tmppath = filepath.Join(dir, "main"+outext)
err := makeESP32FirmareImage(executable, tmppath)
err := makeESPFirmareImage(executable, tmppath, outputBinaryFormat)
if err != nil {
return err
}
Expand Down
79 changes: 52 additions & 27 deletions builder/esp.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ type espImageSegment struct {
data []byte
}

// makeESP32Firmare converts an input ELF file to an image file for the ESP32
// chip. This is a special purpose image format just for the ESP32 chip, and is
// parsed by the on-chip mask ROM bootloader.
// makeESPFirmare converts an input ELF file to an image file for an ESP32 or
// ESP8266 chip. This is a special purpose image format just for the ESP chip
// family, and is parsed by the on-chip mask ROM bootloader.
//
// The following documentation has been used:
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
// https://github.com/espressif/esp-idf/blob/8fbb63c2a701c22ccf4ce249f43aded73e134a34/components/bootloader_support/include/esp_image_format.h#L58
// https://github.com/espressif/esptool/blob/master/esptool.py
func makeESP32FirmareImage(infile, outfile string) error {
func makeESPFirmareImage(infile, outfile, format string) error {
inf, err := elf.Open(infile)
if err != nil {
return err
Expand Down Expand Up @@ -79,26 +79,49 @@ func makeESP32FirmareImage(infile, outfile string) error {
outf := &bytes.Buffer{}

// Image header.
// Details: https://github.com/espressif/esp-idf/blob/8fbb63c2/components/bootloader_support/include/esp_image_format.h#L58
binary.Write(outf, binary.LittleEndian, struct {
magic uint8
segment_count uint8
spi_mode uint8
spi_speed_size uint8
entry_addr uint32
wp_pin uint8
spi_pin_drv [3]uint8
reserved [11]uint8
hash_appended bool
}{
magic: 0xE9,
segment_count: byte(len(segments)),
spi_mode: 0, // irrelevant, replaced by esptool when flashing
spi_speed_size: 0, // spi_speed, spi_size: replaced by esptool when flashing
entry_addr: uint32(inf.Entry),
wp_pin: 0xEE, // disable WP pin
hash_appended: true, // add a SHA256 hash
})
switch format {
case "esp32":
// Header format:
// https://github.com/espressif/esp-idf/blob/8fbb63c2/components/bootloader_support/include/esp_image_format.h#L58
binary.Write(outf, binary.LittleEndian, struct {
magic uint8
segment_count uint8
spi_mode uint8
spi_speed_size uint8
entry_addr uint32
wp_pin uint8
spi_pin_drv [3]uint8
reserved [11]uint8
hash_appended bool
}{
magic: 0xE9,
segment_count: byte(len(segments)),
spi_mode: 0, // irrelevant, replaced by esptool when flashing
spi_speed_size: 0, // spi_speed, spi_size: replaced by esptool when flashing
entry_addr: uint32(inf.Entry),
wp_pin: 0xEE, // disable WP pin
hash_appended: true, // add a SHA256 hash
})
case "esp8266":
// Header format:
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
// Basically a truncated version of the ESP32 header.
binary.Write(outf, binary.LittleEndian, struct {
magic uint8
segment_count uint8
spi_mode uint8
spi_speed_size uint8
entry_addr uint32
}{
magic: 0xE9,
segment_count: byte(len(segments)),
spi_mode: 0, // irrelevant, replaced by esptool when flashing
spi_speed_size: 0x20, // spi_speed, spi_size: replaced by esptool when flashing
entry_addr: uint32(inf.Entry),
})
default:
return fmt.Errorf("builder: unknown binary format %#v, expected esp32 or esp8266", format)
}

// Write all segments to the image.
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format#segment
Expand All @@ -119,9 +142,11 @@ func makeESP32FirmareImage(infile, outfile string) error {
outf.Write(make([]byte, 15-outf.Len()%16))
outf.WriteByte(checksum)

// SHA256 hash (to protect against image corruption, not for security).
hash := sha256.Sum256(outf.Bytes())
outf.Write(hash[:])
if format == "esp32" {
// SHA256 hash (to protect against image corruption, not for security).
hash := sha256.Sum256(outf.Bytes())
outf.Write(hash[:])
}

// Write the image to the output file.
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
Expand Down
2 changes: 1 addition & 1 deletion lib/cmsis-svd
6 changes: 6 additions & 0 deletions src/device/esp/esp8266.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

.section .text.tinygo_scanCurrentStack
.global tinygo_scanCurrentStack
tinygo_scanCurrentStack:
// TODO: save callee saved registers on the stack
j tinygo_scanstack
21 changes: 21 additions & 0 deletions src/machine/board_nodemcu.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// +build nodemcu

// Pinout for the NodeMCU dev kit.

package machine

// GPIO pins on the NodeMCU board.
const (
D0 Pin = 16
D1 Pin = 5
D2 Pin = 4
D3 Pin = 0
D4 Pin = 2
D5 Pin = 14
D6 Pin = 12
D7 Pin = 13
D8 Pin = 15
)

// Onboard blue LED (on the AI-Thinker module).
const LED = D4
159 changes: 159 additions & 0 deletions src/machine/machine_esp8266.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// +build esp8266

package machine

import (
"device/esp"
"runtime/volatile"
)

func CPUFrequency() uint32 {
return 80000000 // 80MHz
}

type PinMode uint8

const (
PinOutput PinMode = iota
PinInput
)

// Pins that are fixed by the chip.
const (
UART_TX_PIN Pin = 1
UART_RX_PIN Pin = 3
)

// Pin functions are not trivial. The below array maps a pin number (GPIO
// number) to the pad as used in the IO mux.
// Tables with the mapping:
// https://www.esp8266.com/wiki/doku.php?id=esp8266_gpio_pin_allocations#pin_functions
// https://www.espressif.com/sites/default/files/documentation/ESP8266_Pin_List_0.xls
var pinPadMapping = [...]uint8{
12: 0,
13: 1,
14: 2,
15: 3,
3: 4,
1: 5,
6: 6,
7: 7,
8: 8,
9: 9,
10: 10,
11: 11,
0: 12,
2: 13,
4: 14,
5: 15,
}

// getPad returns the pad number and the register to configure this pad.
func (p Pin) getPad() (uint8, *volatile.Register32) {
pad := pinPadMapping[p]
var reg *volatile.Register32
switch pad {
case 0:
reg = &esp.IO_MUX.IO_MUX_MTDI
case 1:
reg = &esp.IO_MUX.IO_MUX_MTCK
case 2:
reg = &esp.IO_MUX.IO_MUX_MTMS
case 3:
reg = &esp.IO_MUX.IO_MUX_MTDO
case 4:
reg = &esp.IO_MUX.IO_MUX_U0RXD
case 5:
reg = &esp.IO_MUX.IO_MUX_U0TXD
case 6:
reg = &esp.IO_MUX.IO_MUX_SD_CLK
case 7:
reg = &esp.IO_MUX.IO_MUX_SD_DATA0
case 8:
reg = &esp.IO_MUX.IO_MUX_SD_DATA1
case 9:
reg = &esp.IO_MUX.IO_MUX_SD_DATA2
case 10:
reg = &esp.IO_MUX.IO_MUX_SD_DATA3
case 11:
reg = &esp.IO_MUX.IO_MUX_SD_CMD
case 12:
reg = &esp.IO_MUX.IO_MUX_GPIO0
case 13:
reg = &esp.IO_MUX.IO_MUX_GPIO2
case 14:
reg = &esp.IO_MUX.IO_MUX_GPIO4
case 15:
reg = &esp.IO_MUX.IO_MUX_GPIO5
}
return pad, reg
}

// Configure sets the given pin as output or input pin.
func (p Pin) Configure(config PinConfig) {
switch config.Mode {
case PinInput, PinOutput:
pad, reg := p.getPad()
if pad >= 12 { // pin 0, 2, 4, 5
reg.Set(0 << 4) // function 0 at bit position 4
} else {
reg.Set(3 << 4) // function 3 at bit position 4
}
if config.Mode == PinOutput {
esp.GPIO.GPIO_ENABLE_W1TS.Set(1 << p)
} else {
esp.GPIO.GPIO_ENABLE_W1TC.Set(1 << p)
}
}
}

// Set sets the output value of this pin to high (true) or low (false).
func (p Pin) Set(value bool) {
if value {
esp.GPIO.GPIO_OUT_W1TS.Set(1 << p)
} else {
esp.GPIO.GPIO_OUT_W1TC.Set(1 << p)
}
}

// Return the register and mask to enable a given GPIO pin. This can be used to
// implement bit-banged drivers.
//
// Warning: only use this on an output pin!
func (p Pin) PortMaskSet() (*uint32, uint32) {
return &esp.GPIO.GPIO_OUT_W1TS.Reg, 1 << p
}

// Return the register and mask to disable a given GPIO pin. This can be used to
// implement bit-banged drivers.
//
// Warning: only use this on an output pin!
func (p Pin) PortMaskClear() (*uint32, uint32) {
return &esp.GPIO.GPIO_OUT_W1TC.Reg, 1 << p
}

// UART0 is a hardware UART that supports both TX and RX.
var UART0 = UART{Buffer: NewRingBuffer()}

type UART struct {
Buffer *RingBuffer
}

// Configure the UART baud rate. TX and RX pins are fixed by the hardware so
// cannot be modified and will be ignored.
func (uart UART) Configure(config UARTConfig) {
if config.BaudRate == 0 {
config.BaudRate = 115200
}
esp.UART0.UART_CLKDIV.Set(CPUFrequency() / config.BaudRate)
}

// WriteByte writes a single byte to the output buffer. Note that the hardware
// includes a buffer of 128 bytes which will be used first.
func (uart UART) WriteByte(c byte) error {
for (esp.UART0.UART_STATUS.Get()>>16)&0xff >= 128 {
// Wait until the TX buffer has room.
}
esp.UART0.UART_FIFO.Set(uint32(c))
return nil
}
Loading