From c534dedf2d2a720fa748a57ce60b823d7a8069fe Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 2 Nov 2016 17:17:28 +0800 Subject: [PATCH] newlib: implement time syscalls --- components/esp32/Kconfig | 29 +++++ components/esp32/cpu_start.c | 3 +- components/esp32/include/soc/frc_timer_reg.h | 49 ++++++++ components/esp32/include/soc/rtc_cntl_reg.h | 3 + components/esp32/include/soc/soc.h | 9 +- .../newlib/platform_include/esp_newlib.h | 8 +- components/newlib/syscall_table.c | 3 +- components/newlib/time.c | 118 +++++++++++++++++- 8 files changed, 211 insertions(+), 11 deletions(-) create mode 100644 components/esp32/include/soc/frc_timer_reg.h diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index b5a8d2f2dd..928b190321 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -319,6 +319,35 @@ config BROWNOUT_DET_RESETDELAY before trying to restart the chip. You can set the delay here. +choice ESP32_TIME_SYSCALL + prompt "Timers used for gettimeofday function" + default ESP32_TIME_SYSCALL_USE_RTC_FRC1 + help + This setting defines which hardware timers are used to + implement 'gettimeofday' function in C library. + + - If only FRC1 timer is used, gettimeofday will provide time at + microsecond resolution. Time will not be preserved when going + into deep sleep mode. + - If both FRC1 and RTC timers are used, timekeeping will + continue in deep sleep. Time will be reported at 1 microsecond + resolution. + - If only RTC timer is used, timekeeping will continue in + deep sleep, but time will be measured at 6.(6) microsecond + resolution. Also the gettimeofday function itself may take + longer to run. + - If no timers are used, gettimeofday function return -1 and + set errno to ENOSYS. + +config ESP32_TIME_SYSCALL_USE_RTC + bool "RTC" +config ESP32_TIME_SYSCALL_USE_RTC_FRC1 + bool "RTC and FRC1" +config ESP32_TIME_SYSCALL_USE_FRC1 + bool "FRC1" +config ESP32_TIME_SYSCALL_USE_NONE + bool "None" +endchoice endmenu diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 8d56e2c12d..c82c528597 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -169,7 +169,8 @@ void start_cpu0_default(void) #if CONFIG_TASK_WDT esp_task_wdt_init(); #endif - esp_setup_syscalls(); + esp_setup_syscall_table(); + esp_setup_time_syscalls(); esp_vfs_dev_uart_register(); esp_reent_init(_GLOBAL_REENT); const char* default_uart_dev = "/dev/uart/0"; diff --git a/components/esp32/include/soc/frc_timer_reg.h b/components/esp32/include/soc/frc_timer_reg.h new file mode 100644 index 0000000000..24b942c0bb --- /dev/null +++ b/components/esp32/include/soc/frc_timer_reg.h @@ -0,0 +1,49 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SOC_FRC_TIMER_REG_H_ +#define _SOC_FRC_TIMER_REG_H_ + +#include "soc.h" + +/** + * These are the register definitions for "legacy" timers + */ + +#define REG_FRC_TIMER_BASE(i) (DR_REG_FRC_TIMER_BASE + i*0x20) + +#define FRC_TIMER_LOAD_REG(i) (REG_FRC_TIMER_BASE(i) + 0x0) // timer load value (23 bit for i==0, 32 bit for i==1) +#define FRC_TIMER_LOAD_VALUE(i) ((i == 0)?0x007FFFFF:0xffffffff) +#define FRC_TIMER_LOAD_VALUE_S 0 + +#define FRC_TIMER_COUNT_REG(i) (REG_FRC_TIMER_BASE(i) + 0x4) // timer count value (23 bit for i==0, 32 bit for i==1) +#define FRC_TIMER_COUNT ((i == 0)?0x007FFFFF:0xffffffff) +#define FRC_TIMER_COUNT_S 0 + +#define FRC_TIMER_CTRL_REG(i) (REG_FRC_TIMER_BASE(i) + 0x8) +#define FRC_TIMER_INT_ENABLE (BIT(8)) // enable interrupt +#define FRC_TIMER_ENABLE (BIT(7)) // enable timer +#define FRC_TIMER_AUTOLOAD (BIT(6)) // enable autoload +#define FRC_TIMER_PRESCALER 0x00000007 // 0: divide by 1, 2: divide by 16, 4: divide by 256 +#define FRC_TIMER_PRESCALER_S 1 +#define FRC_TIMER_EDGE_INT (BIT(0)) // 0: level, 1: edge + +#define FRC_TIMER_INT_REG(i) (REG_FRC_TIMER_BASE(i) + 0xC) +#define FRC_TIMER_INT_CLR (BIT(0)) // clear interrupt + +#define FRC_TIMER_ALARM_REG(i) (REG_FRC_TIMER_BASE(i) + 0x10) // timer alarm value; register only present for i == 1 +#define FRC_TIMER_ALARM 0xFFFFFFFF +#define FRC_TIMER_ALARM_S 0 + +#endif //_SOC_FRC_TIMER_REG_H_ diff --git a/components/esp32/include/soc/rtc_cntl_reg.h b/components/esp32/include/soc/rtc_cntl_reg.h index bb4e2afced..d99cec1864 100644 --- a/components/esp32/include/soc/rtc_cntl_reg.h +++ b/components/esp32/include/soc/rtc_cntl_reg.h @@ -239,6 +239,9 @@ #define RTC_CNTL_TIME_VALID_V 0x1 #define RTC_CNTL_TIME_VALID_S 30 +/* frequency of RTC slow clock, Hz */ +#define RTC_CTNL_SLOWCLK_FREQ 150000 + #define RTC_CNTL_TIME0_REG (DR_REG_RTCCNTL_BASE + 0x10) /* RTC_CNTL_TIME_LO : RO ;bitpos:[31:0] ;default: 32'h0 ; */ /*description: RTC timer low 32 bits*/ diff --git a/components/esp32/include/soc/soc.h b/components/esp32/include/soc/soc.h index 4ffdfb069e..8ab9f027c5 100755 --- a/components/esp32/include/soc/soc.h +++ b/components/esp32/include/soc/soc.h @@ -148,6 +148,7 @@ #define DR_REG_GPIO_SD_BASE 0x3ff44f00 #define DR_REG_FE2_BASE 0x3ff45000 #define DR_REG_FE_BASE 0x3ff46000 +#define DR_REG_FRC_TIMER_BASE 0x3ff47000 #define DR_REG_RTCCNTL_BASE 0x3ff48000 #define DR_REG_RTCIO_BASE 0x3ff48400 #define DR_REG_SARADC_BASE 0x3ff48800 @@ -281,9 +282,9 @@ * 19 2 extern level * 20 2 extern level * 21 2 extern level - * 22 3 extern edge + * 22 3 extern edge FRC1 timer * 23 3 extern level - * 24 4 extern level + * 24 4 extern level TG1_WDT * 25 4 extern level Reserved Reserved * 26 5 extern level Reserved Reserved * 27 3 extern level Reserved Reserved @@ -301,8 +302,10 @@ #define ETS_T0_WDT_INUM 3 #define ETS_WBB_INUM 4 #define ETS_TG0_T1_INUM 10 /**< use edge interrupt*/ +#define ETS_FRC1_INUM 22 +#define ETS_T1_WDT_INUM 24 -//CPU0 Intrrupt number used in ROM, should be cancelled in SDK +//CPU0 Interrupt number used in ROM, should be cancelled in SDK #define ETS_SLC_INUM 1 #define ETS_UART0_INUM 5 #define ETS_UART1_INUM 5 diff --git a/components/newlib/platform_include/esp_newlib.h b/components/newlib/platform_include/esp_newlib.h index 468f2ae34f..eac3544259 100644 --- a/components/newlib/platform_include/esp_newlib.h +++ b/components/newlib/platform_include/esp_newlib.h @@ -31,7 +31,13 @@ void esp_reent_init(struct _reent* r); * Called from the startup code, not intended to be called from application * code. */ -void esp_setup_syscalls(); +void esp_setup_syscall_table(); +/** + * Initialize hardware timer used as time source for newlib time functions. + * + * Called from the startup code, not intended to be called from application. + */ +void esp_setup_time_syscalls(); #endif //__ESP_NEWLIB_H__ diff --git a/components/newlib/syscall_table.c b/components/newlib/syscall_table.c index b6414af554..feed768172 100644 --- a/components/newlib/syscall_table.c +++ b/components/newlib/syscall_table.c @@ -24,6 +24,7 @@ #include #include "rom/libc_stubs.h" #include "esp_vfs.h" +#include "esp_newlib.h" static struct _reent s_reent; @@ -79,7 +80,7 @@ static struct syscall_stub_table s_stub_table = { ._scanf_float = &_scanf_float, }; -void esp_setup_syscalls() +void esp_setup_syscall_table() { syscall_table_ptr_pro = &s_stub_table; syscall_table_ptr_app = &s_stub_table; diff --git a/components/newlib/time.c b/components/newlib/time.c index 021b295451..83645aa4cc 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -14,22 +14,130 @@ #include #include +#include +#include #include #include #include #include #include "esp_attr.h" +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/frc_timer_reg.h" +#include "rom/ets_sys.h" +#include "freertos/FreeRTOS.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "sdkconfig.h" +#if defined( CONFIG_ESP32_TIME_SYSCALL_USE_RTC ) || defined( CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 ) +#define WITH_RTC 1 +#endif -clock_t _times_r(struct _reent *r, struct tms *ptms) +#if defined( CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 ) || defined( CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 ) +#define WITH_FRC1 1 +#endif + +#ifdef WITH_RTC +static uint64_t get_rtc_time_us() { - __errno_r(r) = ENOSYS; - return (clock_t) -1; + SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M); + while (GET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID_M) == 0) { + ; + } + CLEAR_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M); + uint64_t low = READ_PERI_REG(RTC_CNTL_TIME0_REG); + uint64_t high = READ_PERI_REG(RTC_CNTL_TIME1_REG); + uint64_t ticks = (high << 32) | low; + return ticks * 100 / (RTC_CTNL_SLOWCLK_FREQ / 10000); // scale RTC_CTNL_SLOWCLK_FREQ to avoid overflow +} +#endif // WITH_RTC + + +#ifdef WITH_FRC1 +#define FRC1_PRESCALER 16 +#define FRC1_PRESCALER_CTL 2 +#define FRC1_TICK_FREQ (APB_CLK_FREQ / FRC1_PRESCALER) +#define FRC1_TICKS_PER_US (FRC1_TICK_FREQ / 1000000) +#define FRC1_ISR_PERIOD_US (FRC_TIMER_LOAD_VALUE(0) / FRC1_TICKS_PER_US) +// Counter frequency will be APB_CLK_FREQ / 16 = 5 MHz +// 1 tick = 0.2 us +// Timer has 23 bit counter, so interrupt will fire each 1677721.6 microseconds. +// This is not a whole number, so timer will drift by 0.3 ppm due to rounding error. + +static volatile uint64_t s_microseconds = 0; + +static void IRAM_ATTR frc_timer_isr() +{ + WRITE_PERI_REG(FRC_TIMER_INT_REG(0), FRC_TIMER_INT_CLR); + s_microseconds += FRC1_ISR_PERIOD_US; } -// TODO: read time from RTC -int _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) +#endif // WITH_FRC1 + +void esp_setup_time_syscalls() { +#if defined( WITH_FRC1 ) +#if defined( WITH_RTC ) + // initialize time from RTC clock + s_microseconds = get_rtc_time_us(); +#endif //WITH_RTC + + + // set up timer + WRITE_PERI_REG(FRC_TIMER_CTRL_REG(0), \ + FRC_TIMER_AUTOLOAD | \ + (FRC1_PRESCALER_CTL << FRC_TIMER_PRESCALER_S) | \ + FRC_TIMER_EDGE_INT); + + WRITE_PERI_REG(FRC_TIMER_LOAD_REG(0), FRC_TIMER_LOAD_VALUE(0)); + SET_PERI_REG_MASK(FRC_TIMER_CTRL_REG(0), + FRC_TIMER_ENABLE | \ + FRC_TIMER_INT_ENABLE); + intr_matrix_set(xPortGetCoreID(), ETS_TIMER1_INTR_SOURCE, ETS_FRC1_INUM); + xt_set_interrupt_handler(ETS_FRC1_INUM, &frc_timer_isr, NULL); + xt_ints_on(1 << ETS_FRC1_INUM); +#endif // WITH_FRC1 +} + +clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms) +{ + clock_t t = xTaskGetTickCount() * (portTICK_PERIOD_MS * CLK_TCK / 1000); + ptms->tms_cstime = t; + ptms->tms_cutime = 0; + ptms->tms_stime = t; + ptms->tms_utime = 0; + struct timeval tv = {0, 0}; + _gettimeofday_r(r, &tv, NULL); + return (clock_t) tv.tv_sec; +} + +int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) +{ + (void) tz; +#ifdef WITH_FRC1 + uint32_t timer_ticks_before = READ_PERI_REG(FRC_TIMER_COUNT_REG(0)); + uint64_t microseconds = s_microseconds; + uint32_t timer_ticks_after = READ_PERI_REG(FRC_TIMER_COUNT_REG(0)); + if (timer_ticks_after > timer_ticks_before) { + // overflow happened at some point between getting + // timer_ticks_before and timer_ticks_after + // microseconds value is ambiguous, get a new one + microseconds = s_microseconds; + } + microseconds += (FRC_TIMER_LOAD_VALUE(0) - timer_ticks_after) / FRC1_TICKS_PER_US; +#elif defined(WITH_RTC) + uint64_t microseconds = get_rtc_time_us(); +#endif + +#if defined( WITH_FRC1 ) || defined( WITH_RTC ) + if (tv) { + tv->tv_sec = microseconds / 1000000; + tv->tv_usec = microseconds % 1000000; + } + return 0; +#else __errno_r(r) = ENOSYS; return -1; +#endif // defined( WITH_FRC1 ) || defined( WITH_RTC ) } -- 2.40.0