Skip to content

Commit 28d2c71

Browse files
committed
esp8266: add support for this chip
Many thanks to cnlohr for the nosdk8266 project: https://github.com/cnlohr/nosdk8266
1 parent 39433a3 commit 28d2c71

File tree

10 files changed

+468
-3
lines changed

10 files changed

+468
-3
lines changed

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ gen-device-avr:
129129
build/gen-device-svd: ./tools/gen-device-svd/*.go
130130
$(GO) build -o $@ ./tools/gen-device-svd/
131131

132+
gen-device-esp: build/gen-device-svd
133+
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif-Community -interrupts=software lib/cmsis-svd/data/Espressif-Community/ src/device/esp/
134+
GO111MODULE=off $(GO) fmt ./src/device/esp
135+
132136
gen-device-nrf: build/gen-device-svd
133137
./build/gen-device-svd -source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk lib/nrfx/mdk/ src/device/nrf/
134138
GO111MODULE=off $(GO) fmt ./src/device/nrf
@@ -156,13 +160,13 @@ gen-device-stm32: build/gen-device-svd
156160

157161
# Get LLVM sources.
158162
$(LLVM_PROJECTDIR)/README.md:
159-
git clone -b release/10.x --depth=1 https://github.com/llvm/llvm-project $(LLVM_PROJECTDIR)
163+
git clone -b xtensa_release_10.x --depth=1 https://github.com/espressif/llvm-project $(LLVM_PROJECTDIR)
160164
llvm-source: $(LLVM_PROJECTDIR)/README.md
161165

162166
# Configure LLVM.
163167
TINYGO_SOURCE_DIR=$(shell pwd)
164168
$(LLVM_BUILDDIR)/build.ninja: llvm-source
165-
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF $(LLVM_OPTION)
169+
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF $(LLVM_OPTION)
166170

167171
# Build LLVM.
168172
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja

src/device/esp/esp8266.S

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
.section .text.runtime.getCurrentStackPointer
3+
.global runtime.getCurrentStackPointer
4+
runtime.getCurrentStackPointer:
5+
// The stack pointer is located in a1. Copy it over to the return register
6+
// in a2.
7+
mov a2, a1
8+
ret.n
9+
10+
.section .text.runtime.abort
11+
.global runtime.abort
12+
runtime.abort:
13+
1:
14+
waiti 0 // wait for interrupt
15+
j 1b
16+
17+
.section .text.runtime.disableInterrupts
18+
.global runtime.disableInterrupts
19+
runtime.disableInterrupts:
20+
// Read the previous PS register state and disable all interrupts.
21+
rsil a2, 15
22+
ret.n
23+
24+
.section .text.runtime.restoreInterrupts
25+
.global runtime.restoreInterrupts
26+
runtime.restoreInterrupts:
27+
// Restore interrupts by writing back the previous PS register.
28+
wsr a2, PS
29+
ret.n
30+
31+
.section .text.tinygo_scanCurrentStack
32+
.global tinygo_scanCurrentStack
33+
tinygo_scanCurrentStack:
34+
// TODO: save callee saved registers on the stack
35+
j tinygo_scanstack

src/machine/board_nodemcu.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// +build nodemcu
2+
3+
// Pinout for the NodeMCU dev kit.
4+
5+
package machine
6+
7+
// GPIO pins on the NodeMCU board.
8+
const (
9+
D0 Pin = 16
10+
D1 Pin = 5
11+
D2 Pin = 4
12+
D3 Pin = 0
13+
D4 Pin = 2
14+
D5 Pin = 14
15+
D6 Pin = 12
16+
D7 Pin = 13
17+
D8 Pin = 15
18+
)
19+
20+
// Onboard blue LED (on the AI-Thinker module).
21+
const LED = D4

src/machine/machine_esp8266.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// +build esp8266
2+
3+
package machine
4+
5+
import (
6+
"device/esp"
7+
"runtime/volatile"
8+
)
9+
10+
func CPUFrequency() uint32 {
11+
return 80000000 // 80MHz
12+
}
13+
14+
type PinMode uint8
15+
16+
const (
17+
PinOutput PinMode = iota
18+
PinInput
19+
)
20+
21+
// Pins that are fixed by the chip.
22+
const (
23+
UART_TX_PIN Pin = 1
24+
UART_RX_PIN Pin = 3
25+
)
26+
27+
// Pin functions are not trivial. The below array maps a pin number (GPIO
28+
// number) to the pad as used in the IO mux.
29+
// Tables with the mapping:
30+
// https://www.esp8266.com/wiki/doku.php?id=esp8266_gpio_pin_allocations#pin_functions
31+
// https://www.espressif.com/sites/default/files/documentation/ESP8266_Pin_List_0.xls
32+
var pinPadMapping = [...]uint8{
33+
12: 0,
34+
13: 1,
35+
14: 2,
36+
15: 3,
37+
3: 4,
38+
1: 5,
39+
6: 6,
40+
7: 7,
41+
8: 8,
42+
9: 9,
43+
10: 10,
44+
11: 11,
45+
0: 12,
46+
2: 13,
47+
4: 14,
48+
5: 15,
49+
}
50+
51+
// getPad returns the pad number and the register to configure this pad.
52+
func (p Pin) getPad() (uint8, *volatile.Register32) {
53+
pad := pinPadMapping[p]
54+
var reg *volatile.Register32
55+
switch pad {
56+
case 0:
57+
reg = &esp.IO_MUX.IO_MUX_MTDI
58+
case 1:
59+
reg = &esp.IO_MUX.IO_MUX_MTCK
60+
case 2:
61+
reg = &esp.IO_MUX.IO_MUX_MTMS
62+
case 3:
63+
reg = &esp.IO_MUX.IO_MUX_MTDO
64+
case 4:
65+
reg = &esp.IO_MUX.IO_MUX_U0RXD
66+
case 5:
67+
reg = &esp.IO_MUX.IO_MUX_U0TXD
68+
case 6:
69+
reg = &esp.IO_MUX.IO_MUX_SD_CLK
70+
case 7:
71+
reg = &esp.IO_MUX.IO_MUX_SD_DATA0
72+
case 8:
73+
reg = &esp.IO_MUX.IO_MUX_SD_DATA1
74+
case 9:
75+
reg = &esp.IO_MUX.IO_MUX_SD_DATA2
76+
case 10:
77+
reg = &esp.IO_MUX.IO_MUX_SD_DATA3
78+
case 11:
79+
reg = &esp.IO_MUX.IO_MUX_SD_CMD
80+
case 12:
81+
reg = &esp.IO_MUX.IO_MUX_GPIO0
82+
case 13:
83+
reg = &esp.IO_MUX.IO_MUX_GPIO2
84+
case 14:
85+
reg = &esp.IO_MUX.IO_MUX_GPIO4
86+
case 15:
87+
reg = &esp.IO_MUX.IO_MUX_GPIO5
88+
}
89+
return pad, reg
90+
}
91+
92+
// Configure sets the given pin as output or input pin.
93+
func (p Pin) Configure(config PinConfig) {
94+
switch config.Mode {
95+
case PinInput, PinOutput:
96+
pad, reg := p.getPad()
97+
if pad >= 12 { // pin 0, 2, 4, 5
98+
reg.Set(0 << 4) // function 0 at bit position 4
99+
} else {
100+
reg.Set(3 << 4) // function 3 at bit position 4
101+
}
102+
if config.Mode == PinOutput {
103+
esp.GPIO.GPIO_ENABLE_W1TS.Set(1 << uint8(p))
104+
} else {
105+
esp.GPIO.GPIO_ENABLE_W1TC.Set(1 << uint8(p))
106+
}
107+
}
108+
}
109+
110+
// Set sets the output value of this pin to high (true) or low (false).
111+
func (p Pin) Set(value bool) {
112+
if value {
113+
esp.GPIO.GPIO_OUT_W1TS.Set(1 << uint8(p))
114+
} else {
115+
esp.GPIO.GPIO_OUT_W1TC.Set(1 << uint8(p))
116+
}
117+
}
118+
119+
// UART0 is a hardware UART that supports both TX and RX.
120+
var UART0 = UART{Buffer: NewRingBuffer()}
121+
122+
type UART struct {
123+
Buffer *RingBuffer
124+
}
125+
126+
// Configure the UART baud rate. TX and RX pins are fixed by the hardware so
127+
// cannot be modified and will be ignored.
128+
func (uart UART) Configure(config UARTConfig) {
129+
if config.BaudRate == 0 {
130+
config.BaudRate = 115200
131+
}
132+
esp.UART0.UART_CLKDIV.Set(CPUFrequency() / config.BaudRate)
133+
}
134+
135+
// WriteByte writes a single byte to the output buffer. Note that the hardware
136+
// includes a buffer of 128 bytes which will be used first.
137+
func (uart UART) WriteByte(c byte) {
138+
for (esp.UART0.UART_STATUS.Get()>>16)&0xff >= 128 {
139+
// Wait until the TX buffer has room.
140+
}
141+
esp.UART0.UART_FIFO.Set(uint32(c))
142+
}

src/machine/uart.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// +build avr nrf sam sifive stm32 k210 nxp
1+
// +build avr esp8266 nrf sam sifive stm32 k210 nxp
22

33
package machine
44

src/runtime/arch_xtensa.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// +build xtensa
2+
3+
package runtime
4+
5+
const GOARCH = "arm" // xtensa pretends to be arm
6+
7+
// The bitness of the CPU (e.g. 8, 32, 64).
8+
const TargetBits = 32
9+
10+
// Align on a word boundary.
11+
func align(ptr uintptr) uintptr {
12+
return (ptr + 3) &^ 3
13+
}
14+
15+
func getCurrentStackPointer() uintptr

src/runtime/runtime_esp8266.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// +build esp8266
2+
3+
package runtime
4+
5+
import (
6+
"device/esp"
7+
"machine"
8+
"unsafe"
9+
)
10+
11+
type timeUnit int64
12+
13+
var currentTime timeUnit = 0
14+
15+
func putchar(c byte) {
16+
machine.UART0.WriteByte(c)
17+
}
18+
19+
// Write to the internal control bus (using I2C?).
20+
// Signature found here:
21+
// https://github.com/espressif/esp-idf/blob/64654c04447914610586e1ac44457510a5bf7191/components/soc/esp32/i2c_rtc_clk.h#L32
22+
//export rom_i2c_writeReg
23+
func rom_i2c_writeReg(block, host_id, reg_add, data uint8)
24+
25+
func postinit() {}
26+
27+
//export main
28+
func main() {
29+
// Clear .bss section. .data has already been loaded by the ROM bootloader.
30+
preinit()
31+
32+
// Initialize PLL.
33+
// I'm not quite sure what this magic incantation means, but it does set the
34+
// esp8266 to the right clock speed. Without this, it is running too slow.
35+
rom_i2c_writeReg(103, 4, 1, 136)
36+
rom_i2c_writeReg(103, 4, 2, 145)
37+
38+
// Initialize UART.
39+
machine.UART0.Configure(machine.UARTConfig{})
40+
41+
// Initialize timer. Bits:
42+
// 7: timer enable
43+
// 6: automatically reload when hitting 0
44+
// 3-2: divide by 256
45+
esp.TIMER.FRC1_CTRL.Set(1<<7 | 1<<6 | 2<<2)
46+
esp.TIMER.FRC1_LOAD.Set(0x3fffff) // set all 22 bits to 1
47+
esp.TIMER.FRC1_COUNT.Set(0x3fffff) // set all 22 bits to 1
48+
49+
run()
50+
51+
// Fallback: if main ever returns, hang the CPU.
52+
abort()
53+
}
54+
55+
//go:extern _sbss
56+
var _sbss [0]byte
57+
58+
//go:extern _ebss
59+
var _ebss [0]byte
60+
61+
func preinit() {
62+
// Initialize .bss: zero-initialized global variables.
63+
ptr := unsafe.Pointer(&_sbss)
64+
for ptr != unsafe.Pointer(&_ebss) {
65+
*(*uint32)(ptr) = 0
66+
ptr = unsafe.Pointer(uintptr(ptr) + 4)
67+
}
68+
}
69+
70+
func ticks() timeUnit {
71+
// Get the counter value of the timer. It is 22 bits and starts with all
72+
// ones (0x3fffff). To make it easier to work with, let it count upwards.
73+
count := 0x3fffff - esp.TIMER.FRC1_COUNT.Get()
74+
75+
// Replace the lowest 22 bits of the current time with the counter.
76+
newTime := (currentTime &^ 0x3fffff) | timeUnit(count)
77+
78+
// If there was an overflow, the new time will be lower than the current
79+
// time, so will need to add (1<<22).
80+
if newTime < currentTime {
81+
newTime += 0x400000
82+
}
83+
84+
// Update the timestamp for the next call to ticks().
85+
currentTime = newTime
86+
87+
return currentTime
88+
}
89+
90+
const asyncScheduler = false
91+
92+
const tickNanos = 3200 // time.Second / (80MHz / 256)
93+
94+
func ticksToNanoseconds(ticks timeUnit) int64 {
95+
return int64(ticks) * tickNanos
96+
}
97+
98+
func nanosecondsToTicks(ns int64) timeUnit {
99+
return timeUnit(ns / tickNanos)
100+
}
101+
102+
// sleepTicks busy-waits until the given number of ticks have passed.
103+
func sleepTicks(d timeUnit) {
104+
sleepUntil := ticks() + d
105+
for ticks() < sleepUntil {
106+
}
107+
}
108+
109+
func abort()
110+
111+
// Implement memset for LLVM and compiler-rt.
112+
//go:export memset
113+
func libc_memset(ptr unsafe.Pointer, c byte, size uintptr) {
114+
for i := uintptr(0); i < size; i++ {
115+
*(*byte)(unsafe.Pointer(uintptr(ptr) + i)) = c
116+
}
117+
}

targets/esp8266.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"llvm-target": "xtensa",
3+
"cpu": "esp8266",
4+
"goos": "linux",
5+
"goarch": "arm",
6+
"build-tags": ["esp8266", "esp", "xtensa", "baremetal", "linux", "arm"],
7+
"compiler": "clang",
8+
"linker": "xtensa-lx106-elf-ld",
9+
"cflags": [
10+
"-g",
11+
"--target=xtensa",
12+
"-mcpu=esp8266",
13+
"-Oz",
14+
"-Werror",
15+
"-fshort-enums",
16+
"-Wno-macro-redefined",
17+
"-Qunused-arguments",
18+
"-fno-exceptions", "-fno-unwind-tables",
19+
"-ffunction-sections", "-fdata-sections"
20+
],
21+
"ldflags": [
22+
"--gc-sections"
23+
],
24+
"linkerscript": "targets/esp8266.ld",
25+
"extra-files": [
26+
"src/device/esp/esp8266.S"
27+
]
28+
}

0 commit comments

Comments
 (0)