diff --git a/firmware/targets/f7/Inc/FreeRTOSConfig.h b/firmware/targets/f7/Inc/FreeRTOSConfig.h index f0944cb7..c3b2117c 100644 --- a/firmware/targets/f7/Inc/FreeRTOSConfig.h +++ b/firmware/targets/f7/Inc/FreeRTOSConfig.h @@ -18,7 +18,7 @@ extern uint32_t SystemCoreClock; #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ (SystemCoreClock) -#define configTICK_RATE_HZ ((TickType_t)1024) +#define configTICK_RATE_HZ ((TickType_t)1000) #define configMAX_PRIORITIES (56) #define configMINIMAL_STACK_SIZE ((uint16_t)128) diff --git a/firmware/targets/f7/Src/main.c b/firmware/targets/f7/Src/main.c index 2e3bf04f..759626a2 100644 --- a/firmware/targets/f7/Src/main.c +++ b/firmware/targets/f7/Src/main.c @@ -6,32 +6,31 @@ #define TAG "Main" -#ifdef FURI_RAM_EXEC -int main() { - // Initialize FURI layer - furi_init(); - - // Flipper critical FURI HAL - furi_hal_init_early(); +static const osThreadAttr_t init_thread_attr = { + .name = "Init", + .stack_size = 4096, +}; +void init_task() { // Flipper FURI HAL furi_hal_init(); // Init flipper flipper_init(); - furi_run(); - - while(1) { - } + osThreadExit(); } -#else + int main() { // Initialize FURI layer furi_init(); // Flipper critical FURI HAL furi_hal_init_early(); + +#ifdef FURI_RAM_EXEC + osThreadNew(init_task, NULL, &init_thread_attr); +#else furi_hal_light_sequence("RGB"); // Delay is for button sampling @@ -52,21 +51,16 @@ int main() { furi_hal_power_reset(); } else { furi_hal_light_sequence("rgb G"); - - // Flipper FURI HAL - furi_hal_init(); - - // Init flipper - flipper_init(); - - furi_run(); + osThreadNew(init_task, NULL, &init_thread_attr); } - - while(1) { - } -} #endif + // Run Kernel + furi_run(); + + furi_crash("Kernel is Dead"); +} + void Error_Handler(void) { furi_crash("ErrorHandler"); } diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index a2e78bfe..b3362ba8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -10,6 +10,8 @@ void furi_hal_init_early() { furi_hal_clock_init_early(); furi_hal_delay_init(); + furi_hal_os_init(); + furi_hal_resources_init_early(); furi_hal_spi_init_early(); @@ -75,9 +77,6 @@ void furi_hal_init() { furi_hal_bt_init(); furi_hal_compress_icon_init(); - // FreeRTOS glue - furi_hal_os_init(); - // FatFS driver initialization MX_FATFS_Init(); FURI_LOG_I(TAG, "FATFS OK"); diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index ca5e5649..02264422 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -9,13 +9,15 @@ #define TAG "FuriHalClock" -#define TICK_INT_PRIORITY 0U +#define CPU_CLOCK_HZ_EARLY 4000000 +#define CPU_CLOCK_HZ_MAIN 64000000 +#define TICK_INT_PRIORITY 15U #define HS_CLOCK_IS_READY() (LL_RCC_HSE_IsReady() && LL_RCC_HSI_IsReady()) #define LS_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady()) void furi_hal_clock_init_early() { - LL_Init1msTick(4000000); - LL_SetSystemCoreClock(4000000); + LL_SetSystemCoreClock(CPU_CLOCK_HZ_EARLY); + LL_Init1msTick(SystemCoreClock); LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB); @@ -27,6 +29,8 @@ void furi_hal_clock_init_early() { LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1); LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); + LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2); + LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3); } @@ -47,7 +51,7 @@ void furi_hal_clock_deinit_early() { } void furi_hal_clock_init() { - /* Prepare Flash memory for 64mHz system clock */ + /* Prepare Flash memory for 64MHz system clock */ LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) ; @@ -116,12 +120,13 @@ void furi_hal_clock_init() { ; /* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */ - LL_SetSystemCoreClock(64000000); + LL_SetSystemCoreClock(CPU_CLOCK_HZ_MAIN); /* Update the time base */ - LL_InitTick(64000000, 1000); + LL_Init1msTick(SystemCoreClock); LL_SYSTICK_EnableIT(); - NVIC_SetPriority(SysTick_IRQn, TICK_INT_PRIORITY); + NVIC_SetPriority( + SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), TICK_INT_PRIORITY, 0)); NVIC_EnableIRQ(SysTick_IRQn); LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); @@ -175,7 +180,6 @@ void furi_hal_clock_init() { // APB1 GRP2 LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1); - LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2); // APB2 // LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC); @@ -217,3 +221,11 @@ void furi_hal_clock_switch_to_pll() { while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) ; } + +void furi_hal_clock_suspend_tick() { + CLEAR_BIT(SysTick->CTRL, SysTick_CTRL_ENABLE_Msk); +} + +void furi_hal_clock_resume_tick() { + SET_BIT(SysTick->CTRL, SysTick_CTRL_ENABLE_Msk); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.h b/firmware/targets/f7/furi_hal/furi_hal_clock.h index 0a1099ac..9cb11db4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.h +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.h @@ -14,3 +14,9 @@ void furi_hal_clock_switch_to_hsi(); /** Switch to PLL clock */ void furi_hal_clock_switch_to_pll(); + +/** Stop SysTick counter without resetting */ +void furi_hal_clock_suspend_tick(); + +/** Continue SysTick counter operation */ +void furi_hal_clock_resume_tick(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_delay.c b/firmware/targets/f7/furi_hal/furi_hal_delay.c index 94ef7865..99636230 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_delay.c +++ b/firmware/targets/f7/furi_hal/furi_hal_delay.c @@ -6,8 +6,6 @@ #define TAG "FuriHalDelay" -static volatile uint32_t tick_cnt = 0; - void furi_hal_delay_init() { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; @@ -18,12 +16,8 @@ uint32_t furi_hal_delay_instructions_per_microsecond() { return SystemCoreClock / 1000000; } -void furi_hal_tick(void) { - tick_cnt++; -} - uint32_t furi_hal_get_tick(void) { - return tick_cnt; + return osKernelGetTickCount(); } uint32_t furi_hal_ms_to_ticks(float milliseconds) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h b/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h new file mode 100644 index 00000000..61afd631 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include + +// Timer used for tickless idle +#define FURI_HAL_IDLE_TIMER_MAX 0xFFFF +#define FURI_HAL_IDLE_TIMER LPTIM2 +#define FURI_HAL_IDLE_TIMER_IRQ LPTIM2_IRQn + +static inline void furi_hal_idle_timer_init() { + // Configure clock source + LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE); + // Set interrupt priority and enable them + NVIC_SetPriority( + FURI_HAL_IDLE_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0)); + NVIC_EnableIRQ(FURI_HAL_IDLE_TIMER_IRQ); +} + +static inline void furi_hal_idle_timer_start(uint32_t count) { + count--; + // Enable timer + LL_LPTIM_Enable(FURI_HAL_IDLE_TIMER); + while(!LL_LPTIM_IsEnabled(FURI_HAL_IDLE_TIMER)) + ; + + // Enable compare match interrupt + LL_LPTIM_EnableIT_CMPM(FURI_HAL_IDLE_TIMER); + + // Set compare, autoreload and start counter + // Include some marging to workaround ARRM behaviour + LL_LPTIM_SetCompare(FURI_HAL_IDLE_TIMER, count - 3); + LL_LPTIM_SetAutoReload(FURI_HAL_IDLE_TIMER, count); + LL_LPTIM_StartCounter(FURI_HAL_IDLE_TIMER, LL_LPTIM_OPERATING_MODE_ONESHOT); +} + +static inline void furi_hal_idle_timer_reset() { + // Hard reset timer + // THE ONLY RELIABLE WAY to stop it according to errata + LL_LPTIM_DeInit(FURI_HAL_IDLE_TIMER); + // Prevent IRQ handler call + NVIC_ClearPendingIRQ(FURI_HAL_IDLE_TIMER_IRQ); +} + +static inline uint32_t furi_hal_idle_timer_get_cnt() { + uint32_t counter = LL_LPTIM_GetCounter(FURI_HAL_IDLE_TIMER); + uint32_t counter_shadow = LL_LPTIM_GetCounter(FURI_HAL_IDLE_TIMER); + while(counter != counter_shadow) { + counter = counter_shadow; + counter_shadow = LL_LPTIM_GetCounter(FURI_HAL_IDLE_TIMER); + } + return counter; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c index e9fc023b..d122fbc7 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c @@ -1,5 +1,6 @@ #include "furi_hal_interrupt.h" #include "furi_hal_delay.h" +#include "furi_hal_os.h" #include @@ -249,7 +250,7 @@ extern void HW_IPCC_Tx_Handler(); extern void HW_IPCC_Rx_Handler(); void SysTick_Handler(void) { - furi_hal_tick(); + furi_hal_os_tick(); } void USB_LP_IRQHandler(void) { @@ -264,4 +265,4 @@ void IPCC_C1_TX_IRQHandler(void) { void IPCC_C1_RX_IRQHandler(void) { HW_IPCC_Rx_Handler(); -} \ No newline at end of file +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.c b/firmware/targets/f7/furi_hal/furi_hal_os.c index 6ef5a9f4..e282645b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_os.c +++ b/firmware/targets/f7/furi_hal/furi_hal_os.c @@ -1,17 +1,22 @@ #include -#include +#include #include +#include +#include #include #include #define TAG "FuriHalOs" -#define FURI_HAL_OS_CLK_FREQUENCY 32768 -#define FURI_HAL_OS_TICK_PER_SECOND 1024 -#define FURI_HAL_OS_CLK_PER_TICK (FURI_HAL_OS_CLK_FREQUENCY / FURI_HAL_OS_TICK_PER_SECOND) -#define FURI_HAL_OS_TICK_PER_EPOCH (FURI_HAL_OS_TIMER_MAX / FURI_HAL_OS_CLK_PER_TICK) -#define FURI_HAL_OS_MAX_SLEEP (FURI_HAL_OS_TICK_PER_EPOCH - 1) +#define FURI_HAL_IDLE_TIMER_CLK_HZ 32768 +#define FURI_HAL_OS_TICK_HZ configTICK_RATE_HZ + +#define FURI_HAL_OS_IDLE_CNT_TO_TICKS(x) ((x * FURI_HAL_OS_TICK_HZ) / FURI_HAL_IDLE_TIMER_CLK_HZ) +#define FURI_HAL_OS_TICKS_TO_IDLE_CNT(x) ((x * FURI_HAL_IDLE_TIMER_CLK_HZ) / FURI_HAL_OS_TICK_HZ) + +#define FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH (FURI_HAL_OS_IDLE_CNT_TO_TICKS(FURI_HAL_IDLE_TIMER_MAX)) +#define FURI_HAL_OS_MAX_SLEEP (FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH - 1) #ifdef FURI_HAL_OS_DEBUG #include @@ -30,48 +35,37 @@ void furi_hal_os_timer_callback() { extern void xPortSysTickHandler(); -volatile uint32_t furi_hal_os_skew = 0; +static volatile uint32_t furi_hal_os_skew = 0; void furi_hal_os_init() { - LL_DBGMCU_APB1_GRP2_FreezePeriph(LL_DBGMCU_APB1_GRP2_LPTIM2_STOP); - - furi_hal_os_timer_init(); - furi_hal_os_timer_continuous(FURI_HAL_OS_CLK_PER_TICK); + furi_hal_idle_timer_init(); #ifdef FURI_HAL_OS_DEBUG LL_GPIO_SetPinMode(LED_SLEEP_PORT, LED_SLEEP_PIN, LL_GPIO_MODE_OUTPUT); LL_GPIO_SetPinMode(LED_TICK_PORT, LED_TICK_PIN, LL_GPIO_MODE_OUTPUT); LL_GPIO_SetPinMode(LED_SECOND_PORT, LED_SECOND_PIN, LL_GPIO_MODE_OUTPUT); osTimerId_t second_timer = osTimerNew(furi_hal_os_timer_callback, osTimerPeriodic, NULL, NULL); - osTimerStart(second_timer, FURI_HAL_OS_TICK_PER_SECOND); + osTimerStart(second_timer, FURI_HAL_OS_TICK_HZ); #endif FURI_LOG_I(TAG, "Init OK"); } -void LPTIM2_IRQHandler(void) { - // Autoreload - if(LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_OS_TIMER)) { - LL_LPTIM_ClearFLAG_ARRM(FURI_HAL_OS_TIMER); - if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { +void furi_hal_os_tick() { + if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { #ifdef FURI_HAL_OS_DEBUG - LL_GPIO_TogglePin(LED_TICK_PORT, LED_TICK_PIN); + LL_GPIO_TogglePin(LED_TICK_PORT, LED_TICK_PIN); #endif - xPortSysTickHandler(); - } - } - if(LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_OS_TIMER)) { - LL_LPTIM_ClearFLAG_CMPM(FURI_HAL_OS_TIMER); + xPortSysTickHandler(); } } static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) { // Stop ticks - furi_hal_os_timer_reset(); - LL_SYSTICK_DisableIT(); + furi_hal_clock_suspend_tick(); // Start wakeup timer - furi_hal_os_timer_single(expected_idle_ticks * FURI_HAL_OS_CLK_PER_TICK); + furi_hal_idle_timer_start(FURI_HAL_OS_TICKS_TO_IDLE_CNT(expected_idle_ticks)); #ifdef FURI_HAL_OS_DEBUG LL_GPIO_ResetOutputPin(LED_SLEEP_PORT, LED_SLEEP_PIN); @@ -85,21 +79,19 @@ static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) { #endif // Calculate how much time we spent in the sleep - uint32_t after_cnt = furi_hal_os_timer_get_cnt() + furi_hal_os_skew; - uint32_t after_tick = after_cnt / FURI_HAL_OS_CLK_PER_TICK; - furi_hal_os_skew = after_cnt % FURI_HAL_OS_CLK_PER_TICK; + uint32_t after_cnt = furi_hal_idle_timer_get_cnt() + furi_hal_os_skew; + uint32_t after_tick = FURI_HAL_OS_IDLE_CNT_TO_TICKS(after_cnt); + furi_hal_os_skew = after_cnt - (after_cnt / after_tick); - bool cmpm = LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_OS_TIMER); - bool arrm = LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_OS_TIMER); + bool cmpm = LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_IDLE_TIMER); + bool arrm = LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_IDLE_TIMER); if(cmpm && arrm) after_tick += expected_idle_ticks; // Prepare tick timer for new round - furi_hal_os_timer_reset(); + furi_hal_idle_timer_reset(); // Resume ticks - LL_SYSTICK_EnableIT(); - furi_hal_os_timer_continuous(FURI_HAL_OS_CLK_PER_TICK); - + furi_hal_clock_resume_tick(); return after_tick; } @@ -109,7 +101,7 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { return; } - // Limit mount of ticks to maximum that timer can count + // Limit amount of ticks to maximum that timer can count if(expected_idle_ticks > FURI_HAL_OS_MAX_SLEEP) { expected_idle_ticks = FURI_HAL_OS_MAX_SLEEP; } @@ -125,14 +117,9 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { // Sleep and track how much ticks we spent sleeping uint32_t completed_ticks = furi_hal_os_sleep(expected_idle_ticks); - // Notify system about time spent in sleep if(completed_ticks > 0) { - if(completed_ticks > expected_idle_ticks) { - vTaskStepTick(expected_idle_ticks); - } else { - vTaskStepTick(completed_ticks); - } + vTaskStepTick(MIN(completed_ticks, expected_idle_ticks)); } // Reenable IRQ diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.h b/firmware/targets/f7/furi_hal/furi_hal_os.h index 0b3d1580..e13b2c25 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_os.h +++ b/firmware/targets/f7/furi_hal/furi_hal_os.h @@ -11,6 +11,10 @@ extern "C" { */ void furi_hal_os_init(); +/* Advance OS tick counter + */ +void furi_hal_os_tick(); + #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_os_timer.h b/firmware/targets/f7/furi_hal/furi_hal_os_timer.h deleted file mode 100644 index dd01ac60..00000000 --- a/firmware/targets/f7/furi_hal/furi_hal_os_timer.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -// Timer used for system ticks -#define FURI_HAL_OS_TIMER_MAX 0xFFFF -#define FURI_HAL_OS_TIMER_REG_LOAD_DLY 0x1 -#define FURI_HAL_OS_TIMER LPTIM2 -#define FURI_HAL_OS_TIMER_IRQ LPTIM2_IRQn - -static inline void furi_hal_os_timer_init() { - // Configure clock source - LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE); - // Set interrupt priority and enable them - NVIC_SetPriority( - FURI_HAL_OS_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0)); - NVIC_EnableIRQ(FURI_HAL_OS_TIMER_IRQ); -} - -static inline void furi_hal_os_timer_continuous(uint32_t count) { - count--; - // Enable timer - LL_LPTIM_Enable(FURI_HAL_OS_TIMER); - while(!LL_LPTIM_IsEnabled(FURI_HAL_OS_TIMER)) - ; - - // Enable rutoreload match interrupt - LL_LPTIM_EnableIT_ARRM(FURI_HAL_OS_TIMER); - - // Set autoreload and start counter - LL_LPTIM_SetAutoReload(FURI_HAL_OS_TIMER, count); - LL_LPTIM_StartCounter(FURI_HAL_OS_TIMER, LL_LPTIM_OPERATING_MODE_CONTINUOUS); -} - -static inline void furi_hal_os_timer_single(uint32_t count) { - count--; - // Enable timer - LL_LPTIM_Enable(FURI_HAL_OS_TIMER); - while(!LL_LPTIM_IsEnabled(FURI_HAL_OS_TIMER)) - ; - - // Enable compare match interrupt - LL_LPTIM_EnableIT_CMPM(FURI_HAL_OS_TIMER); - - // Set compare, autoreload and start counter - // Include some marging to workaround ARRM behaviour - LL_LPTIM_SetCompare(FURI_HAL_OS_TIMER, count - 3); - LL_LPTIM_SetAutoReload(FURI_HAL_OS_TIMER, count); - LL_LPTIM_StartCounter(FURI_HAL_OS_TIMER, LL_LPTIM_OPERATING_MODE_ONESHOT); -} - -static inline void furi_hal_os_timer_reset() { - // Hard reset timer - // THE ONLY RELIABLEWAY to stop it according to errata - LL_LPTIM_DeInit(FURI_HAL_OS_TIMER); -} - -static inline uint32_t furi_hal_os_timer_get_cnt() { - uint32_t counter = LL_LPTIM_GetCounter(FURI_HAL_OS_TIMER); - uint32_t counter_shadow = LL_LPTIM_GetCounter(FURI_HAL_OS_TIMER); - while(counter != counter_shadow) { - counter = counter_shadow; - counter_shadow = LL_LPTIM_GetCounter(FURI_HAL_OS_TIMER); - } - return counter; -} diff --git a/firmware/targets/furi_hal_include/furi_hal_delay.h b/firmware/targets/furi_hal_include/furi_hal_delay.h index f34a7053..8d88a4c1 100644 --- a/firmware/targets/furi_hal_include/furi_hal_delay.h +++ b/firmware/targets/furi_hal_include/furi_hal_delay.h @@ -18,11 +18,6 @@ void furi_hal_delay_init(); /** Get instructions per microsecond count */ uint32_t furi_hal_delay_instructions_per_microsecond(); -/** Increase tick counter. - * Should be called from SysTick ISR - */ -void furi_hal_tick(void); - /** Get current tick counter * * System uptime, may overflow.