mirror of https://github.com/tinygo-org/tinygo.git
Browse Source
Many thanks to cnlohr for the nosdk8266 project: https://github.com/cnlohr/nosdk8266pull/1372/head
Ayke van Laethem
5 years ago
committed by
Ron Evans
11 changed files with 489 additions and 31 deletions
@ -1 +1 @@ |
|||
Subproject commit 2fc335802cf97309ec4035caf276746b53efbd5b |
|||
Subproject commit d9b58694cef35b39ddf61c07ef7e6347d6ec3cbd |
@ -0,0 +1,6 @@ |
|||
|
|||
.section .text.tinygo_scanCurrentStack |
|||
.global tinygo_scanCurrentStack |
|||
tinygo_scanCurrentStack: |
|||
// TODO: save callee saved registers on the stack |
|||
j tinygo_scanstack |
@ -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 |
@ -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 |
|||
} |
@ -0,0 +1,115 @@ |
|||
// +build esp8266
|
|||
|
|||
package runtime |
|||
|
|||
import ( |
|||
"device" |
|||
"device/esp" |
|||
"machine" |
|||
"unsafe" |
|||
) |
|||
|
|||
type timeUnit int64 |
|||
|
|||
var currentTime timeUnit = 0 |
|||
|
|||
func putchar(c byte) { |
|||
machine.UART0.WriteByte(c) |
|||
} |
|||
|
|||
// Write to the internal control bus (using I2C?).
|
|||
// Signature found here:
|
|||
// https://github.com/espressif/ESP8266_RTOS_SDK/blob/14171de0/components/esp8266/include/esp8266/rom_functions.h#L54
|
|||
//export rom_i2c_writeReg
|
|||
func rom_i2c_writeReg(block, host_id, reg_add, data uint8) |
|||
|
|||
func postinit() {} |
|||
|
|||
//export main
|
|||
func main() { |
|||
// Clear .bss section. .data has already been loaded by the ROM bootloader.
|
|||
preinit() |
|||
|
|||
// Initialize PLL.
|
|||
// I'm not quite sure what this magic incantation means, but it does set the
|
|||
// esp8266 to the right clock speed. Without this, it is running too slow.
|
|||
rom_i2c_writeReg(103, 4, 1, 136) |
|||
rom_i2c_writeReg(103, 4, 2, 145) |
|||
|
|||
// Initialize UART.
|
|||
machine.UART0.Configure(machine.UARTConfig{}) |
|||
|
|||
// Initialize timer. Bits:
|
|||
// ENABLE: timer enable
|
|||
// ROLLOVER: automatically reload when hitting 0
|
|||
// PRESCALE: divide by 256
|
|||
esp.TIMER.FRC1_CTRL.Set( |
|||
esp.TIMER_FRC1_CTRL_TIMER_ENABLE | esp.TIMER_FRC1_CTRL_ROLLOVER | esp.TIMER_FRC1_CTRL_PRESCALE_DIVIDER_DEVIDED_BY_256<<esp.TIMER_FRC1_CTRL_PRESCALE_DIVIDER_Pos) |
|||
esp.TIMER.FRC1_LOAD.Set(0x3fffff) // set all 22 bits to 1
|
|||
esp.TIMER.FRC1_COUNT.Set(0x3fffff) // set all 22 bits to 1
|
|||
|
|||
run() |
|||
|
|||
// Fallback: if main ever returns, hang the CPU.
|
|||
abort() |
|||
} |
|||
|
|||
//go:extern _sbss
|
|||
var _sbss [0]byte |
|||
|
|||
//go:extern _ebss
|
|||
var _ebss [0]byte |
|||
|
|||
func preinit() { |
|||
// Initialize .bss: zero-initialized global variables.
|
|||
ptr := unsafe.Pointer(&_sbss) |
|||
for ptr != unsafe.Pointer(&_ebss) { |
|||
*(*uint32)(ptr) = 0 |
|||
ptr = unsafe.Pointer(uintptr(ptr) + 4) |
|||
} |
|||
} |
|||
|
|||
func ticks() timeUnit { |
|||
// Get the counter value of the timer. It is 22 bits and starts with all
|
|||
// ones (0x3fffff). To make it easier to work with, let it count upwards.
|
|||
count := 0x3fffff - esp.TIMER.FRC1_COUNT.Get() |
|||
|
|||
// Replace the lowest 22 bits of the current time with the counter.
|
|||
newTime := (currentTime &^ 0x3fffff) | timeUnit(count) |
|||
|
|||
// If there was an overflow, the new time will be lower than the current
|
|||
// time, so will need to add (1<<22).
|
|||
if newTime < currentTime { |
|||
newTime += 0x400000 |
|||
} |
|||
|
|||
// Update the timestamp for the next call to ticks().
|
|||
currentTime = newTime |
|||
|
|||
return currentTime |
|||
} |
|||
|
|||
const asyncScheduler = false |
|||
|
|||
const tickNanos = 3200 // time.Second / (80MHz / 256)
|
|||
|
|||
func ticksToNanoseconds(ticks timeUnit) int64 { |
|||
return int64(ticks) * tickNanos |
|||
} |
|||
|
|||
func nanosecondsToTicks(ns int64) timeUnit { |
|||
return timeUnit(ns / tickNanos) |
|||
} |
|||
|
|||
// sleepTicks busy-waits until the given number of ticks have passed.
|
|||
func sleepTicks(d timeUnit) { |
|||
sleepUntil := ticks() + d |
|||
for ticks() < sleepUntil { |
|||
} |
|||
} |
|||
|
|||
func abort() { |
|||
for { |
|||
device.Asm("waiti 0") |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
{ |
|||
"inherits": ["xtensa"], |
|||
"cpu": "esp8266", |
|||
"build-tags": ["esp8266", "esp"], |
|||
"linker": "xtensa-esp32-elf-ld", |
|||
"cflags": [ |
|||
"-mcpu=esp8266" |
|||
], |
|||
"linkerscript": "targets/esp8266.ld", |
|||
"extra-files": [ |
|||
"src/device/esp/esp8266.S" |
|||
], |
|||
"binary-format": "esp8266", |
|||
"flash-command": "esptool.py --chip=esp8266 --port {port} write_flash 0x00000 {bin} -fm qio" |
|||
} |
@ -0,0 +1,109 @@ |
|||
/* Linker script for the ESP8266 */ |
|||
|
|||
MEMORY |
|||
{ |
|||
/* Data RAM. Allows byte access. */ |
|||
DRAM (rw) : ORIGIN = 0x3FFE8000, LENGTH = 80K |
|||
/* Instruction RAM. */ |
|||
IRAM (x) : ORIGIN = 0x40100000, LENGTH = 32K |
|||
} |
|||
|
|||
/* The entry point. It is set in the image flashed to the chip, so must be |
|||
* defined. |
|||
*/ |
|||
ENTRY(main) |
|||
|
|||
SECTIONS |
|||
{ |
|||
/* Mutable global variables. |
|||
*/ |
|||
.data : ALIGN(4) |
|||
{ |
|||
_sdata = ABSOLUTE(.); |
|||
*(.data) |
|||
*(.data.*) |
|||
} >DRAM |
|||
|
|||
/* Constant global variables. |
|||
* Note that they still need to be loaded in RAM because the ESP8266 doesn't |
|||
* allow byte access to flash. |
|||
*/ |
|||
.rodata : ALIGN(4) |
|||
{ |
|||
*(.rodata) |
|||
*(.rodata.*) |
|||
} >DRAM |
|||
|
|||
/* Global variables that are mutable and zero-initialized. |
|||
*/ |
|||
.bss (NOLOAD) : ALIGN(4) |
|||
{ |
|||
. = ALIGN (4); |
|||
_sbss = ABSOLUTE(.); |
|||
*(.bss) |
|||
*(.bss.*) |
|||
. = ALIGN (4); |
|||
_ebss = ABSOLUTE(.); |
|||
} >DRAM |
|||
|
|||
/* Constant literals and code. Loaded into IRAM for now. Eventually, most |
|||
* code should be executed directly from flash. |
|||
* Note that literals must be before code for the l32r instruction to work. |
|||
*/ |
|||
.text : ALIGN(4) |
|||
{ |
|||
*(.literal .text) |
|||
*(.literal.* .text.*) |
|||
} >IRAM |
|||
} |
|||
|
|||
_globals_start = _sdata; |
|||
_globals_end = _ebss; |
|||
_heap_start = _ebss; |
|||
_heap_end = ORIGIN(DRAM) + LENGTH(DRAM); |
|||
|
|||
/* It appears that the stack is set to 0x3ffffff0 when main is called. |
|||
* Be conservative and scan all the way up to the end of the RAM. |
|||
*/ |
|||
_stack_top = 0x40000000; |
|||
|
|||
/* Functions normally provided by a libc. |
|||
* Source: |
|||
* https://github.com/espressif/ESP8266_NONOS_SDK/blob/master/ld/eagle.rom.addr.v6.ld |
|||
*/ |
|||
memcpy = 0x4000df48; |
|||
memmove = 0x4000e04c; |
|||
memset = 0x4000e190; |
|||
|
|||
/* Compiler runtime functions provided by the ROM. |
|||
* Source: |
|||
* https://github.com/espressif/ESP8266_NONOS_SDK/blob/master/ld/eagle.rom.addr.v6.ld |
|||
*/ |
|||
__adddf3 = 0x4000c538; |
|||
__addsf3 = 0x4000c180; |
|||
__divdf3 = 0x4000cb94; |
|||
__divdi3 = 0x4000ce60; |
|||
__divsi3 = 0x4000dc88; |
|||
__extendsfdf2 = 0x4000cdfc; |
|||
__fixdfsi = 0x4000ccb8; |
|||
__fixunsdfsi = 0x4000cd00; |
|||
__fixunssfsi = 0x4000c4c4; |
|||
__floatsidf = 0x4000e2f0; |
|||
__floatsisf = 0x4000e2ac; |
|||
__floatunsidf = 0x4000e2e8; |
|||
__floatunsisf = 0x4000e2a4; |
|||
__muldf3 = 0x4000c8f0; |
|||
__muldi3 = 0x40000650; |
|||
__mulsf3 = 0x4000c3dc; |
|||
__subdf3 = 0x4000c688; |
|||
__subsf3 = 0x4000c268; |
|||
__truncdfsf2 = 0x4000cd5c; |
|||
__udivdi3 = 0x4000d310; |
|||
__udivsi3 = 0x4000e21c; |
|||
__umoddi3 = 0x4000d770; |
|||
__umodsi3 = 0x4000e268; |
|||
__umulsidi3 = 0x4000dcf0; |
|||
|
|||
/* Proprietary ROM function needed for proper clock configuration. |
|||
*/ |
|||
rom_i2c_writeReg = 0x400072d8; |
@ -0,0 +1,4 @@ |
|||
{ |
|||
"inherits": ["esp8266"], |
|||
"build-tags": ["nodemcu"] |
|||
} |
Loading…
Reference in new issue