1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/esp32/startup.c
Jens Alfke ce1fe776cf cpu/esp32: allow explicit ESP32 crystal freq configuration
Some ESP32 boards (like my SparkFun ESP32 Thing) have a main clock
crystal that runs at 26MHz, not 40MHz. RIOT appears to assume 40MHz.
The mismatch causes the UART to not sync properly, resulting in
garbage written to the terminal instead of log output.

I’ve added:

* A new board configuration constant ESP32_XTAL_FREQ that defaults
  to 40, but can be overridden by a board def or at build time to
  force a specific value (i.e. 26).
* Some code spliced into system_clk_init() to check this constant and
  call rtc_clk_init() to set the correct frequency.
* A copy of the rtf_clk_init() function from the ESP-IDF sources.

Fixes #10272
2018-10-30 16:42:07 -07:00

378 lines
11 KiB
C

/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp32
* @{
*
* @file
* @brief Implementation of the CPU initialization
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @author Jens Alfke <jens@mooseyard.com>
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "esp_common.h"
#include <stdlib.h>
#include <string.h>
#include <sys/reent.h>
#include "board.h"
#include "esp_attr.h"
#include "exceptions.h"
#include "irq_arch.h"
#include "kernel_defines.h"
#include "kernel_init.h"
#include "log.h"
#include "syscalls.h"
#include "thread_arch.h"
#include "periph/cpuid.h"
#include "periph/init.h"
#include "periph/rtc.h"
#include "driver/periph_ctrl.h"
#include "esp/common_macros.h"
#include "heap/esp_heap_caps_init.h"
#include "rom/cache.h"
#include "rom/ets_sys.h"
#include "rom/rtc.h"
#include "rom/uart.h"
#include "soc/apb_ctrl_reg.h"
#include "soc/cpu.h"
#include "soc/dport_reg.h"
#include "soc/dport_access.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_cntl_struct.h"
#include "soc/timer_group_struct.h"
#include "xtensa/core-macros.h"
#include "xtensa/xtensa_api.h"
#include "periph_cpu.h"
#include "tools.h"
#ifdef MODULE_STDIO_UART
#include "stdio_uart.h"
#endif
#define MHZ 1000000UL
#define STRINGIFY(s) STRINGIFY2(s)
#define STRINGIFY2(s) #s
/* following variables are defined in linker script */
extern uint8_t _bss_start;
extern uint8_t _bss_end;
extern uint8_t _sheap;
extern uint8_t _eheap;
extern uint8_t _rtc_bss_start;
extern uint8_t _rtc_bss_end;
extern uint8_t _rtc_bss_rtc_start;
extern uint8_t _rtc_bss_rtc_end;
extern uint8_t _init_start;
/* external esp function declarations */
extern void esp_clk_init(void);
extern void esp_perip_clk_init(void);
extern void esp_reent_init(struct _reent* r);
extern void esp_panic_wdt_stop (void);
extern void spi_ram_init(void);
extern void spi_ram_heap_init(void);
extern uint32_t hwrand (void);
extern void bootloader_clock_configure(void);
/* forward declarations */
static void system_init(void);
static void do_global_ctors(void);
static void intr_matrix_clear(void);
typedef int32_t esp_err_t;
/**
* @brief CPU startup function
*
* This function is the entry point in the user application. It is called
* after a system reset to startup the system.
*/
NORETURN void IRAM call_start_cpu0 (void)
{
register uint32_t *sp __asm__ ("a1"); (void)sp;
cpu_configure_region_protection();
/* move exception vectors to IRAM */
asm volatile ("wsr %0, vecbase\n" ::"r"(&_init_start));
RESET_REASON reset_reason = rtc_get_reset_reason(PRO_CPU_NUM);
/* reset from panic handler by RWDT or TG0WDT */
if (reset_reason == RTCWDT_SYS_RESET || reset_reason == TG0WDT_SYS_RESET) {
esp_panic_wdt_stop();
}
/* Clear BSS. Please do not attempt to do any complex stuff */
/* (like early logging) before this. */
memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start));
/* if we are not waking up from deep sleep, clear RTC bss */
if (reset_reason != DEEPSLEEP_RESET) {
memset(&_rtc_bss_start, 0, (&_rtc_bss_end - &_rtc_bss_start));
}
/* initialize RTC data after power on */
if (reset_reason == POWERON_RESET || reset_reason == RTCWDT_RTC_RESET) {
memset(&_rtc_bss_rtc_start, 0, (&_rtc_bss_rtc_end - &_rtc_bss_rtc_start));
}
uint8_t cpu_id[CPUID_LEN];
cpuid_get ((void*)cpu_id);
ets_printf("\nStarting ESP32 with ID: ");
for (unsigned i = 0; i < CPUID_LEN; i++) {
ets_printf("%02x", cpu_id[i]);
}
ets_printf("\n\nCurrent clocks in Hz: CPU=%d APB=%d XTAL=%d SLOW=%d\n",
rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()),
rtc_clk_apb_freq_get(), rtc_clk_xtal_freq_get()*MHZ,
rtc_clk_slow_freq_get_hz());
#if ENABLE_DEBUG
ets_printf("reset reason: %d\n", reset_reason);
ets_printf("_stack %p\n", sp);
ets_printf("_bss_start %p\n", &_bss_start);
ets_printf("_bss_end %p\n", &_bss_end);
#ifndef MODULE_ESP_IDF_HEAP
ets_printf("_heap_start %p\n", &_sheap);
ets_printf("_heap_end %p\n", &_eheap);
ets_printf("_heap_free %u\n", get_free_heap_size());
#endif /* MODULE_ESP_IDF_HEAP */
#endif /* ENABLE_DEBUG */
ets_printf("PRO cpu is up ");
/* disable APP cpu */
ets_printf("(single core mode, only PRO cpu is used)\n");
DPORT_CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN);
#ifdef MODULE_ESP_IDF_HEAP
/* init heap */
heap_caps_init();
#ifdef ENABLE_DEBUG
ets_printf("Heap free: %u byte\n", get_free_heap_size());
#endif /* ENABLE_DEBUG */
#endif /* MODULE_ESP_IDF_HEAP */
/* init SPI RAM if enabled */
#if CONFIG_SPIRAM_SUPPORT && CONFIG_SPIRAM_BOOT_INIT
spi_ram_init();
#endif
ets_printf("PRO cpu starts user code\n");
system_init();
UNREACHABLE();
}
#define RTC_FAST_FREQ_8M_MHZ 8000000
#define rtc_select_slow_clk select_rtc_slow_clk
extern uint32_t esp_clk_slowclk_cal_get(void);
extern void IRAM_ATTR rtc_select_slow_clk(rtc_slow_freq_t slow_clk);
static void IRAM system_clk_init (void)
{
/* first initialize RTC with default configuration */
rtc_config_t rtc_cfg = RTC_CONFIG_DEFAULT();
rtc_init_module(rtc_cfg);
/* configure main crystal frequency if necessary */
if (CONFIG_ESP32_XTAL_FREQ != RTC_XTAL_FREQ_AUTO
&& CONFIG_ESP32_XTAL_FREQ != rtc_clk_xtal_freq_get()) {
bootloader_clock_configure();
}
/* set FAST_CLK to internal low power clock of 8 MHz */
rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M);
/* set SLOW_CLK to internal low power clock of 150 kHz */
rtc_select_slow_clk(RTC_SLOW_FREQ_RTC);
/* wait until UART is idle to avoid loosing output */
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
ets_printf("Switching system clocks can lead to some unreadable characters\n");
ets_printf("This message is usually not visible at the console\n");
/* determine configured CPU clock frequency from sdk_conf.h */
rtc_cpu_freq_t freq;
switch (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) {
case 40: freq = RTC_CPU_FREQ_XTAL; /* derived from external cristal */
break; /* normally 40 MHz */
case 80: freq = RTC_CPU_FREQ_80M; /* derived from PLL */
break;
case 160: freq = RTC_CPU_FREQ_160M; /* derived from PLL */
break;
case 240: freq = RTC_CPU_FREQ_240M; /* derived from PLL */
break;
default: freq = RTC_CPU_FREQ_2M; /* frequencies <= 8 MHz are
set to 2 MHz and handled later */
}
uint32_t freq_before = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ ;
/* set configured CPU frequency */
rtc_clk_cpu_freq_set(freq);
/* Recalculate the ccount to make time calculation correct. */
uint32_t freq_after = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before );
}
extern void IRAM_ATTR thread_yield_isr(void* arg);
static NORETURN void IRAM system_init (void)
{
/* enable cached read from flash */
Cache_Read_Enable(PRO_CPU_NUM);
/* initialize the ISR stack for usage measurements */
thread_isr_stack_init();
/* initialize clocks (CPU_CLK, APB_CLK, SLOW and FAST) */
system_clk_init();
/* disable clocks of peripherals that are not needed at startup */
esp_perip_clk_init();
/* set configured console UART baudrate */
const int uart_clk_freq = rtc_clk_apb_freq_get();
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
uart_div_modify(CONFIG_CONSOLE_UART_NUM,
(uart_clk_freq << 4) / STDIO_UART_BAUDRATE);
/* initialize system call tables of ESP32 rom and newlib */
syscalls_init();
/* initialize the RTC module (restore timer values from RTC RAM) */
rtc_init();
/* install execption handlers */
init_exceptions();
/* clear interrupt matrix */
intr_matrix_clear();
/* systemwide UART initialization */
extern void uart_system_init (void);
uart_system_init();
/* Disable the hold flag of all RTC GPIO pins */
RTCCNTL.hold_force.val = 0;
/* initialize newlib data structure */
esp_reent_init(_GLOBAL_REENT);
_GLOBAL_REENT->_stdin = (FILE*) &__sf_fake_stdin;
_GLOBAL_REENT->_stdout = (FILE*) &__sf_fake_stdout;
_GLOBAL_REENT->_stderr = (FILE*) &__sf_fake_stderr;
/* execute constructors */
do_global_ctors();
/* init watchdogs */
system_wdt_init();
/* init random number generator */
srand(hwrand());
#if defined(MODULE_NEWLIB_SYSCALLS_DEFAULT)
/*
* initialization as it should be called from newlibc (includes the
* execution of stdio_init)
*/
extern void _init(void);
_init();
#elif defined(MODULE_STDIO_UART)
stdio_init();
#endif
/* add SPI RAM to heap if enabled */
#if CONFIG_SPIRAM_SUPPORT && CONFIG_SPIRAM_BOOT_INIT
spi_ram_heap_init();
#endif
/* print some infos */
ets_printf("Used clocks in Hz: CPU=%d APB=%d XTAL=%d FAST=%d SLOW=%d\n",
rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()),
rtc_clk_apb_freq_get(), rtc_clk_xtal_freq_get()*MHZ,
RTC_FAST_FREQ_8M_MHZ, rtc_clk_slow_freq_get_hz());
ets_printf("XTAL calibration value: %d\n", esp_clk_slowclk_cal_get());
ets_printf("Heap free: %u bytes\n", get_free_heap_size());
struct tm _sys_time;
rtc_get_time(&_sys_time);
ets_printf("System time: %04d-%02d-%02d %02d:%02d:%02d\n",
_sys_time.tm_year + 1900, _sys_time.tm_mon + 1, _sys_time.tm_mday,
_sys_time.tm_hour, _sys_time.tm_min, _sys_time.tm_sec);
#if MODULE_MTD
/* init flash drive */
extern void spi_flash_drive_init (void);
spi_flash_drive_init();
#endif
/* initialize the board */
board_init();
/* trigger static peripheral initialization */
periph_init();
/* print the board config */
print_board_config();
/* route a software interrupt source to CPU as trigger for thread yields */
intr_matrix_set(PRO_CPU_NUM, ETS_FROM_CPU_INTR0_SOURCE, CPU_INUM_SOFTWARE);
/* set thread yield handler and enable the software interrupt */
xt_set_interrupt_handler(CPU_INUM_SOFTWARE, thread_yield_isr, NULL);
xt_ints_on(BIT(CPU_INUM_SOFTWARE));
/* initialize ESP system event loop */
extern void esp_event_handler_init(void);
esp_event_handler_init();
/* starting RIOT */
ets_printf("Starting RIOT kernel on PRO cpu\n");
kernel_init();
UNREACHABLE();
}
static void do_global_ctors(void)
{
#if 0 /* TODO when real ctors are used exist */
extern uint32_t* __init_array_start;
extern uint32_t* __init_array_end;
for (uint32_t* up = __init_array_end - 1; up >= __init_array_start; --up) {
void (*fp)(void) = (void (*)(void))up;
fp();
}
#endif
}
static void intr_matrix_clear(void)
{
/* attach all peripheral interrupt sources (Technical Reference, Table 7) */
/* to an arbitrary CPU interrupt number (Technical Reference, Table 8) */
for (int i = ETS_WIFI_MAC_INTR_SOURCE; i <= ETS_CACHE_IA_INTR_SOURCE; i++) {
intr_matrix_set(PRO_CPU_NUM, i, ETS_INVALID_INUM);
}
}