mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-09 13:33:39 +00:00
Add ESP32-S3 support (#5091)
* feat: add initial support for ESP32-S3 (#3442) * feat: add initial support for esp32-s3 * esp32s3: fix merge errors * esp32s3: Fix Watchdog registers bad names * esp32s3: fix linker relocation errors and support for ESP binary * esp32s3: fix memory section overlap * esp32s3: correct clock frequencies * esp32s3: more stable cpu * esp32s3: enable basic gpio support * esp32s3: simplify loading and check extensions * esp32s3: synchronize cpu features with clang * esp32s3: correct iram origin --------- Co-authored-by: Denys Vitali <denys@denv.it> Co-authored-by: Olivier Fauchon <ofauchon2204@gmail.com>
This commit is contained in:
@@ -0,0 +1,312 @@
|
||||
//go:build esp32s3
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/esp"
|
||||
"errors"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const deviceName = esp.Device
|
||||
|
||||
const xtalClock = 40_000000 // 40MHz
|
||||
const apbClock = 80_000000 // 80MHz
|
||||
const cryptoPWMClock = 160_000000 // 160MHz
|
||||
|
||||
// GetCPUFrequency returns the current CPU frequency of the chip.
|
||||
func GetCPUFrequency() (uint32, error) {
|
||||
switch esp.SYSTEM.GetSYSCLK_CONF_SOC_CLK_SEL() {
|
||||
case 0:
|
||||
return xtalClock / (esp.SYSTEM.GetSYSCLK_CONF_PRE_DIV_CNT() + 1), nil
|
||||
case 1:
|
||||
switch esp.SYSTEM.GetCPU_PER_CONF_CPUPERIOD_SEL() {
|
||||
case 0:
|
||||
return 80e6, nil
|
||||
case 1:
|
||||
return 160e6, nil
|
||||
case 2:
|
||||
// If esp.SYSTEM.GetCPU_PER_CONF_PLL_FREQ_SEL() == 1, this is undefined
|
||||
return 240e6, nil
|
||||
}
|
||||
case 2:
|
||||
//RC Fast Clock
|
||||
return (175e5) / (esp.SYSTEM.GetSYSCLK_CONF_PRE_DIV_CNT() + 1), nil
|
||||
}
|
||||
return 0, errors.New("machine: Unable to determine current cpu frequency")
|
||||
}
|
||||
|
||||
// SetCPUFrequency sets the frequency of the CPU to one of several targets
|
||||
func SetCPUFrequency(frequency uint32) error {
|
||||
// Always assume we are on PLL. Lower frequencies can be set with a different
|
||||
// clock source, but this will change the behavior of APB clock and Crypto PWM
|
||||
// clock
|
||||
//esp.SYSTEM.SetSYSCLK_CONF_SOC_CLK_SEL(1)
|
||||
|
||||
switch frequency {
|
||||
case 80_000000:
|
||||
esp.SYSTEM.SetCPU_PER_CONF_CPUPERIOD_SEL(0)
|
||||
esp.SYSTEM.SetCPU_PER_CONF_PLL_FREQ_SEL(0) // Reduce PLL freq when possible
|
||||
return nil
|
||||
case 160_000000:
|
||||
esp.SYSTEM.SetCPU_PER_CONF_CPUPERIOD_SEL(1)
|
||||
esp.SYSTEM.SetCPU_PER_CONF_PLL_FREQ_SEL(0)
|
||||
return nil
|
||||
case 240_000000:
|
||||
esp.SYSTEM.SetCPU_PER_CONF_PLL_FREQ_SEL(1) // Increase PLL freq when needed
|
||||
esp.SYSTEM.SetCPU_PER_CONF_CPUPERIOD_SEL(2)
|
||||
return nil
|
||||
}
|
||||
return errors.New("machine: Unsupported CPU frequency selected. Supported: 80, 160, 240 MHz")
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidSPIBus = errors.New("machine: invalid SPI bus")
|
||||
)
|
||||
|
||||
const (
|
||||
PinOutput PinMode = iota
|
||||
PinInput
|
||||
PinInputPullup
|
||||
PinInputPulldown
|
||||
)
|
||||
|
||||
// Hardware pin numbers
|
||||
const (
|
||||
GPIO0 Pin = 0
|
||||
GPIO1 Pin = 1
|
||||
GPIO2 Pin = 2
|
||||
GPIO3 Pin = 3
|
||||
GPIO4 Pin = 4
|
||||
GPIO5 Pin = 5
|
||||
GPIO6 Pin = 6
|
||||
GPIO7 Pin = 7
|
||||
GPIO8 Pin = 8
|
||||
GPIO9 Pin = 9
|
||||
GPIO10 Pin = 10
|
||||
GPIO11 Pin = 11
|
||||
GPIO12 Pin = 12
|
||||
GPIO13 Pin = 13
|
||||
GPIO14 Pin = 14
|
||||
GPIO15 Pin = 15
|
||||
GPIO16 Pin = 16
|
||||
GPIO17 Pin = 17
|
||||
GPIO18 Pin = 18
|
||||
GPIO19 Pin = 19
|
||||
GPIO20 Pin = 20
|
||||
GPIO21 Pin = 21
|
||||
GPIO26 Pin = 26
|
||||
GPIO27 Pin = 27
|
||||
GPIO28 Pin = 28
|
||||
GPIO29 Pin = 29
|
||||
GPIO30 Pin = 30
|
||||
GPIO31 Pin = 31
|
||||
GPIO32 Pin = 32
|
||||
GPIO33 Pin = 33
|
||||
GPIO34 Pin = 34
|
||||
GPIO35 Pin = 35
|
||||
GPIO36 Pin = 36
|
||||
GPIO37 Pin = 37
|
||||
GPIO38 Pin = 38
|
||||
GPIO39 Pin = 39
|
||||
GPIO40 Pin = 40
|
||||
GPIO41 Pin = 41
|
||||
GPIO42 Pin = 42
|
||||
GPIO43 Pin = 43
|
||||
GPIO44 Pin = 44
|
||||
GPIO45 Pin = 45
|
||||
GPIO46 Pin = 46
|
||||
GPIO47 Pin = 47
|
||||
GPIO48 Pin = 48
|
||||
)
|
||||
|
||||
// Configure this pin with the given configuration.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
// Output function 256 is a special value reserved for use as a regular GPIO
|
||||
// pin. Peripherals (SPI etc) can set a custom output function by calling
|
||||
// lowercase configure() instead with a signal name.
|
||||
p.configure(config, 256)
|
||||
}
|
||||
|
||||
// configure is the same as Configure, but allows for setting a specific input
|
||||
// or output signal.
|
||||
// Signals are always routed through the GPIO matrix for simplicity. Output
|
||||
// signals are configured in FUNCx_OUT_SEL_CFG which selects a particular signal
|
||||
// to output on a given pin. Input signals are configured in FUNCy_IN_SEL_CFG,
|
||||
// which sets the pin to use for a particular input signal.
|
||||
func (p Pin) configure(config PinConfig, signal uint32) {
|
||||
if p == NoPin {
|
||||
// This simplifies pin configuration in peripherals such as SPI.
|
||||
return
|
||||
}
|
||||
|
||||
ioConfig := uint32(0)
|
||||
|
||||
// MCU_SEL: Function 1 is always GPIO
|
||||
ioConfig |= (1 << esp.IO_MUX_GPIO_MCU_SEL_Pos)
|
||||
|
||||
// FUN_IE: Make this pin an input pin (always set for GPIO operation)
|
||||
ioConfig |= esp.IO_MUX_GPIO_FUN_IE
|
||||
|
||||
// DRV: Set drive strength to 20 mA as a default. Pins 17 and 18 are special
|
||||
var drive uint32
|
||||
if p == GPIO17 || p == GPIO18 {
|
||||
drive = 1 // 20 mA
|
||||
} else {
|
||||
drive = 2 // 20 mA
|
||||
}
|
||||
ioConfig |= (drive << esp.IO_MUX_GPIO_FUN_DRV_Pos)
|
||||
|
||||
// WPU/WPD: Select pull mode.
|
||||
if config.Mode == PinInputPullup {
|
||||
ioConfig |= esp.IO_MUX_GPIO_FUN_WPU
|
||||
} else if config.Mode == PinInputPulldown {
|
||||
ioConfig |= esp.IO_MUX_GPIO_FUN_WPD
|
||||
}
|
||||
|
||||
// Set configuration
|
||||
ioRegister := p.ioMuxReg()
|
||||
ioRegister.Set(ioConfig)
|
||||
|
||||
switch config.Mode {
|
||||
case PinOutput:
|
||||
// Set the 'output enable' bit.
|
||||
if p < 32 {
|
||||
esp.GPIO.ENABLE_W1TS.Set(1 << p)
|
||||
} else {
|
||||
esp.GPIO.ENABLE1_W1TS.Set(1 << (p - 32))
|
||||
}
|
||||
// Set the signal to read the output value from. It can be a peripheral
|
||||
// output signal, or the special value 256 which indicates regular GPIO
|
||||
// usage.
|
||||
p.outFunc().Set(signal)
|
||||
case PinInput, PinInputPullup, PinInputPulldown:
|
||||
// Clear the 'output enable' bit.
|
||||
if p < 32 {
|
||||
esp.GPIO.ENABLE_W1TC.Set(1 << p)
|
||||
} else {
|
||||
esp.GPIO.ENABLE1_W1TC.Set(1 << (p - 32))
|
||||
}
|
||||
if signal != 256 {
|
||||
// Signal is a peripheral function (not a simple GPIO). Connect this
|
||||
// signal to the pin.
|
||||
// Note that outFunc and inFunc work in the opposite direction.
|
||||
// outFunc configures a pin to use a given output signal, while
|
||||
// inFunc specifies a pin to use to read the signal from.
|
||||
inFunc(signal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(p)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ioMuxReg returns the IO_MUX_n_REG register used for configuring the io mux for
|
||||
// this pin
|
||||
func (p Pin) ioMuxReg() *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.IO_MUX.GPIO0), uintptr(p)*4))
|
||||
}
|
||||
|
||||
// outFunc returns the FUNCx_OUT_SEL_CFG register used for configuring the
|
||||
// output function selection.
|
||||
func (p Pin) outFunc() *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_OUT_SEL_CFG), uintptr(p)*4))
|
||||
}
|
||||
|
||||
// inFunc returns the FUNCy_IN_SEL_CFG register used for configuring the input
|
||||
// function selection.
|
||||
func inFunc(signal uint32) *volatile.Register32 {
|
||||
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_IN_SEL_CFG), uintptr(signal)*4))
|
||||
}
|
||||
|
||||
// Set the pin to high or low.
|
||||
// Warning: only use this on an output pin!
|
||||
func (p Pin) Set(value bool) {
|
||||
if value {
|
||||
reg, mask := p.portMaskSet()
|
||||
reg.Set(mask)
|
||||
} else {
|
||||
reg, mask := p.portMaskClear()
|
||||
reg.Set(mask)
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
reg, mask := p.portMaskSet()
|
||||
return ®.Reg, mask
|
||||
}
|
||||
|
||||
// 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) {
|
||||
reg, mask := p.portMaskClear()
|
||||
return ®.Reg, mask
|
||||
}
|
||||
|
||||
func (p Pin) portMaskSet() (*volatile.Register32, uint32) {
|
||||
if p < 32 {
|
||||
return &esp.GPIO.OUT_W1TS, 1 << p
|
||||
} else {
|
||||
return &esp.GPIO.OUT1_W1TS, 1 << (p - 32)
|
||||
}
|
||||
}
|
||||
|
||||
func (p Pin) portMaskClear() (*volatile.Register32, uint32) {
|
||||
if p < 32 {
|
||||
return &esp.GPIO.OUT_W1TC, 1 << p
|
||||
} else {
|
||||
return &esp.GPIO.OUT1_W1TC, 1 << (p - 32)
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin when the pin is configured as an
|
||||
// input or as an output.
|
||||
func (p Pin) Get() bool {
|
||||
if p < 32 {
|
||||
return esp.GPIO.IN.Get()&(1<<p) != 0
|
||||
} else {
|
||||
return esp.GPIO.IN1.Get()&(1<<(p-32)) != 0
|
||||
}
|
||||
}
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
var (
|
||||
UART0 = &_UART0
|
||||
_UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
|
||||
UART1 = &_UART1
|
||||
_UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
|
||||
UART2 = &_UART2
|
||||
_UART2 = UART{Bus: esp.UART2, Buffer: NewRingBuffer()}
|
||||
)
|
||||
|
||||
type UART struct {
|
||||
Bus *esp.UART_Type
|
||||
Buffer *RingBuffer
|
||||
}
|
||||
|
||||
func (uart *UART) Configure(config UARTConfig) {
|
||||
if config.BaudRate == 0 {
|
||||
config.BaudRate = 115200
|
||||
}
|
||||
// Crystal clock source is selected by default
|
||||
uart.Bus.CLKDIV.Set(xtalClock / config.BaudRate)
|
||||
}
|
||||
|
||||
func (uart *UART) writeByte(b byte) error {
|
||||
for (uart.Bus.STATUS.Get()>>16)&0xff >= 128 {
|
||||
// Read UART_TXFIFO_CNT from the status register, which indicates how
|
||||
// many bytes there are in the transmit buffer. Wait until there are
|
||||
// less than 128 bytes in this buffer (the default buffer size).
|
||||
}
|
||||
uart.Bus.FIFO.Set(uint32(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) flush() {}
|
||||
|
||||
// TODO: SPI
|
||||
Reference in New Issue
Block a user