Skip to content

Commit 3ee47a9

Browse files
aykevldeadprogram
authored andcommitted
esp: add support for the Espressif ESP32 chip
This is only very minimal support. More support (such as tinygo flash, or peripheral access) should be added in later commits, to keep this one focused. Importantly, this commit changes the LLVM repo from llvm/llvm-project to tinygo-org/llvm-project. This provides a little bit of versioning in case something changes in the Espressif fork. If we want to upgrade to LLVM 11 it's easy to switch back to llvm/llvm-project until Espressif has updated their fork.
1 parent da7db81 commit 3ee47a9

File tree

15 files changed

+520
-39
lines changed

15 files changed

+520
-39
lines changed

.circleci/config.yml

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,28 @@ commands:
4444
command: |
4545
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
4646
sudo apt install ./google-chrome-stable_current_amd64.deb
47+
install-xtensa-toolchain:
48+
parameters:
49+
variant:
50+
type: string
51+
steps:
52+
- run:
53+
name: "Install Xtensa toolchain"
54+
command: |
55+
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
56+
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
57+
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
58+
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
4759
llvm-source-linux:
4860
steps:
4961
- restore_cache:
5062
keys:
51-
- llvm-source-10-v0
63+
- llvm-source-10-v1
5264
- run:
5365
name: "Fetch LLVM source"
5466
command: make llvm-source
5567
- save_cache:
56-
key: llvm-source-10-v0
68+
key: llvm-source-10-v1
5769
paths:
5870
- llvm-project
5971
build-wasi-libc:
@@ -95,7 +107,7 @@ commands:
95107
- lib/wasi-libc/sysroot
96108
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
97109
- run: make gen-device -j4
98-
- run: make smoketest
110+
- run: make smoketest XTENSA=0
99111
- run: make wasmtest
100112
- save_cache:
101113
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
@@ -121,14 +133,16 @@ commands:
121133
gcc-avr \
122134
avr-libc
123135
- install-node
136+
- install-xtensa-toolchain:
137+
variant: "linux-amd64"
124138
- restore_cache:
125139
keys:
126140
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
127141
- go-cache-v2-{{ checksum "go.mod" }}
128142
- llvm-source-linux
129143
- restore_cache:
130144
keys:
131-
- llvm-build-10-linux-v0-assert
145+
- llvm-build-10-linux-v1-assert
132146
- run:
133147
name: "Build LLVM"
134148
command: |
@@ -146,7 +160,7 @@ commands:
146160
make ASSERT=1 llvm-build
147161
fi
148162
- save_cache:
149-
key: llvm-build-10-linux-v0-assert
163+
key: llvm-build-10-linux-v1-assert
150164
paths:
151165
llvm-build
152166
- run: make ASSERT=1
@@ -179,14 +193,16 @@ commands:
179193
gcc-avr \
180194
avr-libc
181195
- install-node
196+
- install-xtensa-toolchain:
197+
variant: "linux-amd64"
182198
- restore_cache:
183199
keys:
184200
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
185201
- go-cache-v2-{{ checksum "go.mod" }}
186202
- llvm-source-linux
187203
- restore_cache:
188204
keys:
189-
- llvm-build-10-linux-v0
205+
- llvm-build-10-linux-v1
190206
- run:
191207
name: "Build LLVM"
192208
command: |
@@ -204,7 +220,7 @@ commands:
204220
make llvm-build
205221
fi
206222
- save_cache:
207-
key: llvm-build-10-linux-v0
223+
key: llvm-build-10-linux-v1
208224
paths:
209225
llvm-build
210226
- build-wasi-libc
@@ -250,23 +266,25 @@ commands:
250266
sudo tar -C /usr/local -xzf go1.14.darwin-amd64.tar.gz
251267
ln -s /usr/local/go/bin/go /usr/local/bin/go
252268
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
269+
- install-xtensa-toolchain:
270+
variant: "macos"
253271
- restore_cache:
254272
keys:
255273
- go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
256274
- go-cache-macos-v2-{{ checksum "go.mod" }}
257275
- restore_cache:
258276
keys:
259-
- llvm-source-10-macos-v0
277+
- llvm-source-10-macos-v1
260278
- run:
261279
name: "Fetch LLVM source"
262280
command: make llvm-source
263281
- save_cache:
264-
key: llvm-source-10-macos-v0
282+
key: llvm-source-10-macos-v1
265283
paths:
266284
- llvm-project
267285
- restore_cache:
268286
keys:
269-
- llvm-build-10-macos-v0
287+
- llvm-build-10-macos-v1
270288
- run:
271289
name: "Build LLVM"
272290
command: |
@@ -278,7 +296,7 @@ commands:
278296
make llvm-build
279297
fi
280298
- save_cache:
281-
key: llvm-build-10-macos-v0
299+
key: llvm-build-10-macos-v1
282300
paths:
283301
llvm-build
284302
- restore_cache:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ docs/_build
33
src/device/avr/*.go
44
src/device/avr/*.ld
55
src/device/avr/*.s
6+
src/device/esp/*.go
67
src/device/nrf/*.go
78
src/device/nrf/*.s
89
src/device/nxp/*.go

Makefile

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ fmt-check:
118118
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
119119

120120

121-
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32 gen-device-kendryte gen-device-nxp
121+
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32 gen-device-kendryte gen-device-nxp
122122

123123
gen-device-avr:
124124
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
@@ -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.0.1 --depth=1 https://github.com/tinygo-org/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
@@ -341,6 +345,9 @@ ifneq ($(AVR), 0)
341345
@$(MD5SUM) test.hex
342346
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
343347
@$(MD5SUM) test.hex
348+
endif
349+
ifneq ($(XTENSA), 0)
350+
$(TINYGO) build -size short -o test.elf -target=esp32 examples/serial
344351
endif
345352
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
346353
@$(MD5SUM) test.hex

azure-pipelines.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,24 @@ jobs:
1414
inputs:
1515
version: '1.15'
1616
- checkout: self
17-
- task: CacheBeta@0
17+
- task: Cache@2
1818
displayName: Cache LLVM source
1919
inputs:
20-
key: llvm-source-10-windows-v0
20+
key: llvm-source-10-windows-v1
2121
path: llvm-project
2222
- task: Bash@3
2323
displayName: Download LLVM source
2424
inputs:
2525
targetType: inline
26-
script: make llvm-source
26+
script: |
27+
make llvm-source
28+
# Workaround for bad symlinks:
29+
# https://github.com/microsoft/azure-pipelines-tasks/issues/13418
30+
rm -f llvm-project/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/bad_symlink
2731
- task: CacheBeta@0
2832
displayName: Cache LLVM build
2933
inputs:
30-
key: llvm-build-10-windows-v0
34+
key: llvm-build-10-windows-v1
3135
path: llvm-build
3236
- task: Bash@3
3337
displayName: Build LLVM
@@ -80,4 +84,4 @@ jobs:
8084
script: |
8185
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
8286
unset GOROOT
83-
make smoketest TINYGO=build/tinygo AVR=0
87+
make smoketest TINYGO=build/tinygo AVR=0 XTENSA=0

src/device/esp/esp32.S

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
// The following definitions were copied from:
3+
// esp-idf/components/xtensa/include/xtensa/corebits.h
4+
#define PS_WOE_MASK 0x00040000
5+
#define PS_OWB_MASK 0x00000F00
6+
#define PS_CALLINC_MASK 0x00030000
7+
#define PS_WOE PS_WOE_MASK
8+
9+
// Only calling it call_start_cpu0 for consistency with ESP-IDF.
10+
.section .text.call_start_cpu0
11+
1:
12+
.long _stack_top
13+
.global call_start_cpu0
14+
call_start_cpu0:
15+
// We need to set the stack pointer to a different value. This is somewhat
16+
// complicated in the Xtensa architecture. The code below is a modified
17+
// version of the following code:
18+
// https://github.com/espressif/esp-idf/blob/c77c4ccf/components/xtensa/include/xt_instr_macros.h#L47
19+
20+
// Disable WOE.
21+
rsr.ps a2
22+
movi a3, ~(PS_WOE_MASK)
23+
and a2, a2, a3
24+
wsr.ps a2
25+
rsync
26+
27+
// Set WINDOWBASE to 1 << WINDOWSTART.
28+
rsr.windowbase a2
29+
ssl a2
30+
movi a2, 1
31+
sll a2, a2
32+
wsr.windowstart a2
33+
rsync
34+
35+
// Load new stack pointer.
36+
l32r sp, 1b
37+
38+
// Re-enable WOE.
39+
rsr.ps a2
40+
movi a3, PS_WOE
41+
or a2, a2, a3
42+
wsr.ps a2
43+
rsync
44+
45+
// Jump to the runtime start function written in Go.
46+
j main
47+
48+
.section .text.tinygo_scanCurrentStack
49+
.global tinygo_scanCurrentStack
50+
tinygo_scanCurrentStack:
51+
// TODO: save callee saved registers on the stack
52+
j tinygo_scanstack

src/machine/machine_esp32.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// +build esp32
2+
3+
package machine
4+
5+
import "device/esp"
6+
7+
const peripheralClock = 80000000 // 80MHz
8+
9+
type PinMode uint8
10+
11+
const (
12+
PinOutput PinMode = iota
13+
PinInput
14+
)
15+
16+
func (p Pin) Set(value bool)
17+
18+
var (
19+
UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
20+
UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
21+
UART2 = UART{Bus: esp.UART2, Buffer: NewRingBuffer()}
22+
)
23+
24+
type UART struct {
25+
Bus *esp.UART_Type
26+
Buffer *RingBuffer
27+
}
28+
29+
func (uart UART) Configure(config UARTConfig) {
30+
if config.BaudRate == 0 {
31+
config.BaudRate = 115200
32+
}
33+
uart.Bus.CLKDIV.Set(peripheralClock / config.BaudRate)
34+
}
35+
36+
func (uart UART) WriteByte(b byte) error {
37+
for (uart.Bus.STATUS.Get()>>16)&0xff >= 128 {
38+
// Read UART_TXFIFO_CNT from the status register, which indicates how
39+
// many bytes there are in the transmit buffer. Wait until there are
40+
// less than 128 bytes in this buffer (the default buffer size).
41+
}
42+
uart.Bus.TX_FIFO.Set(b)
43+
return nil
44+
}

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 esp 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
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// +build xtensa
2+
3+
package interrupt
4+
5+
import "device"
6+
7+
// State represents the previous global interrupt state.
8+
type State uintptr
9+
10+
// Disable disables all interrupts and returns the previous interrupt state. It
11+
// can be used in a critical section like this:
12+
//
13+
// state := interrupt.Disable()
14+
// // critical section
15+
// interrupt.Restore(state)
16+
//
17+
// Critical sections can be nested. Make sure to call Restore in the same order
18+
// as you called Disable (this happens naturally with the pattern above).
19+
func Disable() (state State) {
20+
return State(device.AsmFull("rsil {}, 15", nil))
21+
}
22+
23+
// Restore restores interrupts to what they were before. Give the previous state
24+
// returned by Disable as a parameter. If interrupts were disabled before
25+
// calling Disable, this will not re-enable interrupts, allowing for nested
26+
// cricital sections.
27+
func Restore(state State) {
28+
device.AsmFull("wsr {state}, PS", map[string]interface{}{
29+
"state": state,
30+
})
31+
}

0 commit comments

Comments
 (0)