Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Handle APB frequency change #2250

Merged
merged 13 commits into from
Jan 9, 2019
Merged
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set(CORE_SRCS
cores/esp32/cbuf.cpp
cores/esp32/esp32-hal-adc.c
cores/esp32/esp32-hal-bt.c
cores/esp32/esp32-hal-cpu.c
cores/esp32/esp32-hal-dac.c
cores/esp32/esp32-hal-gpio.c
cores/esp32/esp32-hal-i2c.c
Expand Down
169 changes: 169 additions & 0 deletions cores/esp32/esp32-hal-cpu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// 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.

#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "rom/rtc.h"
#include "esp32-hal.h"
#include "esp32-hal-cpu.h"

typedef struct apb_change_cb_s {
struct apb_change_cb_s * next;
void * arg;
apb_change_cb_t cb;
} apb_change_t;

static apb_change_t * apb_change_callbacks = NULL;
static xSemaphoreHandle apb_change_lock = NULL;

static uint32_t _cpu_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
static uint32_t _sys_time_multiplier = 1;

uint64_t IRAM_ATTR micros64(){
return (uint64_t)(esp_timer_get_time()) * _sys_time_multiplier;
}

//ToDo: figure out how to set FreeRTOS tick properly
void delay(uint32_t ms){
vTaskDelay((ms * _cpu_freq_mhz) / (portTICK_PERIOD_MS * CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ));
}

static uint32_t calculateApb(rtc_cpu_freq_config_t * conf){
if(conf->freq_mhz >= 80){
return 80000000;
}
return (conf->source_freq_mhz * 1000000) / conf->div;
}

static void initApbChangeCallback(){
static volatile bool initialized = false;
if(!initialized){
initialized = true;
apb_change_lock = xSemaphoreCreateMutex();
if(!apb_change_lock){
initialized = false;
}
}
}

static void triggerApbChangeCallback(apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
initApbChangeCallback();
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
apb_change_t * r = apb_change_callbacks;
while(r != NULL){
r->cb(r->arg, ev_type, old_apb, new_apb);
}
xSemaphoreGive(apb_change_lock);
}

bool addApbChangeCallback(void * arg, apb_change_cb_t cb){
initApbChangeCallback();
apb_change_t * c = (apb_change_t*)malloc(sizeof(apb_change_t));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a template class that can be used for linked lists that is generic enough to plug in here, maybe even something from ESP-IDF that can be leveraged instead of managing the list here (and later in remove)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know of one, but I have been thinking of such thing for some time. This is C though... so IDK

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason to stick with C only here, there are already a few C++ pieces here and there...

Copy link
Collaborator

@atanisoft atanisoft Dec 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can also use something like this to mix C++ into C...

template<int signalGenerator>  
void IRAM_ATTR signalGeneratorTimerISR(void)

it has C linkage but you can call it with different values to get different versions of it (this is part of how I do ISRs using one method but two diff data sources)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason why it is C is so the whole HAL can be used in C-only libs and extensions. Not really sure if it's worth it, but it's how it started :)

if(!c){
log_e("Callback Object Malloc Failed");
return false;
}
c->next = NULL;
c->arg = arg;
c->cb = cb;
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
if(apb_change_callbacks == NULL){
apb_change_callbacks = c;
} else {
apb_change_t * r = apb_change_callbacks;
if(r->cb != cb || r->arg != arg){
while(r->next){
r = r->next;
if(r->cb == cb && r->arg == arg){
goto unlock_and_exit;
me-no-dev marked this conversation as resolved.
Show resolved Hide resolved
}
}
r->next = c;
}
}
unlock_and_exit:
xSemaphoreGive(apb_change_lock);
return true;
}

bool removeApbChangeCallback(void * arg, apb_change_cb_t cb){
initApbChangeCallback();
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
apb_change_t * r = apb_change_callbacks;
if(r == NULL){
xSemaphoreGive(apb_change_lock);
return false;
}
if(r->cb == cb && r->arg == arg){
apb_change_callbacks = r->next;
free(r);
} else {
while(r->next && (r->next->cb != cb || r->next->arg != arg)){
r = r->next;
}
if(r->next == NULL || r->next->cb != cb || r->next->arg != arg){
xSemaphoreGive(apb_change_lock);
return false;
}
apb_change_t * c = r->next;
r->next = c->next;
free(c);
}
xSemaphoreGive(apb_change_lock);
return true;
}

bool setCpuFrequency(uint32_t cpu_freq_mhz){
rtc_cpu_freq_config_t conf, cconf;
uint32_t capb, apb;
rtc_clk_cpu_freq_get_config(&cconf);
if(cconf.freq_mhz == cpu_freq_mhz && _cpu_freq_mhz == cpu_freq_mhz){
return true;
}
if(!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)){
log_e("CPU clock could not be set to %u MHz", cpu_freq_mhz);
return false;
}
capb = calculateApb(&cconf);
apb = calculateApb(&conf);
log_i("%s: %u / %u = %u Mhz", (conf.source == RTC_CPU_FREQ_SRC_PLL)?"PLL":((conf.source == RTC_CPU_FREQ_SRC_APLL)?"APLL":((conf.source == RTC_CPU_FREQ_SRC_XTAL)?"XTAL":"8M")), conf.source_freq_mhz, conf.div, conf.freq_mhz);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this may be better at log_d instead of log_i but I'd defer to your judgement on specific level to log this at.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will remove it all together but it's a good test point to ensure that UART flushes before cpu can be changed. nd I often keep log at info so I do not get all unnecessary debug :P

if(capb != apb && apb_change_callbacks){
triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb);
}
rtc_clk_cpu_freq_set_config_fast(&conf);
_cpu_freq_mhz = conf.freq_mhz;
_sys_time_multiplier = 80000000 / apb;
me-no-dev marked this conversation as resolved.
Show resolved Hide resolved
if(capb != apb && apb_change_callbacks){
triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb);
}
return true;
}

uint32_t getCpuFrequency(){
rtc_cpu_freq_config_t conf;
rtc_clk_cpu_freq_get_config(&conf);
return conf.freq_mhz;
}

uint32_t getApbFrequency(){
rtc_cpu_freq_config_t conf;
rtc_clk_cpu_freq_get_config(&conf);
return calculateApb(&conf);
}
49 changes: 49 additions & 0 deletions cores/esp32/esp32-hal-cpu.h
Original file line number Diff line number Diff line change
@@ -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 _ESP32_HAL_CPU_H_
#define _ESP32_HAL_CPU_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

typedef enum { APB_BEFORE_CHANGE, APB_AFTER_CHANGE } apb_change_ev_t;

typedef void (* apb_change_cb_t)(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);

bool addApbChangeCallback(void * arg, apb_change_cb_t cb);
bool removeApbChangeCallback(void * arg, apb_change_cb_t cb);

//function takes the following frequencies as valid values:
// 240, 160, 80 <<< For all XTAL types
// 40, 20, 13, 10, 8, 5, 4, 3, 2, 1 <<< For 40MHz XTAL
// 26, 13, 5, 4, 3, 2, 1 <<< For 26MHz XTAL
// 24, 12, 8, 6, 4, 3, 2, 1 <<< For 24MHz XTAL
bool setCpuFrequency(uint32_t cpu_freq_mhz);

uint32_t getCpuFrequency(); // In MHz
uint32_t getApbFrequency(); // In Hz

uint64_t micros64(); // 64bit version of micros()

#ifdef __cplusplus
}
#endif

#endif /* _ESP32_HAL_CPU_H_ */
53 changes: 4 additions & 49 deletions cores/esp32/esp32-hal-misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,57 +100,12 @@ void disableCore1WDT(){
}
#endif

static uint32_t _cpu_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
static uint32_t _sys_time_multiplier = 1;

bool setCpuFrequency(uint32_t cpu_freq_mhz){
rtc_cpu_freq_config_t conf, cconf;
rtc_clk_cpu_freq_get_config(&cconf);
if(cconf.freq_mhz == cpu_freq_mhz && _cpu_freq_mhz == cpu_freq_mhz){
return true;
}
if(!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)){
log_e("CPU clock could not be set to %u MHz", cpu_freq_mhz);
return false;
}
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
log_i("%s: %u / %u = %u Mhz", (conf.source == RTC_CPU_FREQ_SRC_PLL)?"PLL":((conf.source == RTC_CPU_FREQ_SRC_APLL)?"APLL":((conf.source == RTC_CPU_FREQ_SRC_XTAL)?"XTAL":"8M")), conf.source_freq_mhz, conf.div, conf.freq_mhz);
delay(10);
#endif
rtc_clk_cpu_freq_set_config_fast(&conf);
_cpu_freq_mhz = conf.freq_mhz;
_sys_time_multiplier = 80000000 / getApbFrequency();
return true;
}

uint32_t getCpuFrequency(){
rtc_cpu_freq_config_t conf;
rtc_clk_cpu_freq_get_config(&conf);
return conf.freq_mhz;
}

uint32_t getApbFrequency(){
rtc_cpu_freq_config_t conf;
rtc_clk_cpu_freq_get_config(&conf);
if(conf.freq_mhz >= 80){
return 80000000;
}
return (conf.source_freq_mhz * 1000000) / conf.div;
unsigned long IRAM_ATTR micros(){
return (unsigned long) (micros64());
}

unsigned long IRAM_ATTR micros()
{
return (unsigned long) (esp_timer_get_time()) * _sys_time_multiplier;
}

unsigned long IRAM_ATTR millis()
{
return (unsigned long) (micros() / 1000);
}

void delay(uint32_t ms)
{
vTaskDelay((ms * _cpu_freq_mhz) / (portTICK_PERIOD_MS * 240));
unsigned long IRAM_ATTR millis(){
return (unsigned long) (micros64() / 1000);
}

void IRAM_ATTR delayMicroseconds(uint32_t us)
Expand Down
10 changes: 1 addition & 9 deletions cores/esp32/esp32-hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ void yield(void);
#include "esp32-hal-timer.h"
#include "esp32-hal-bt.h"
#include "esp32-hal-psram.h"
#include "esp32-hal-cpu.h"

#ifndef BOARD_HAS_PSRAM
#ifdef CONFIG_SPIRAM_SUPPORT
Expand All @@ -87,15 +88,6 @@ void enableCore1WDT();
void disableCore1WDT();
#endif

//function takes the following frequencies as valid values:
// 240, 160, 80 <<< For all XTAL types
// 40, 20, 13, 10, 8, 5, 4, 3, 2, 1 <<< For 40MHz XTAL
// 26, 13, 5, 4, 3, 2, 1 <<< For 26MHz XTAL
// 24, 12, 8, 6, 4, 3, 2, 1 <<< For 24MHz XTAL
bool setCpuFrequency(uint32_t cpu_freq_mhz);
uint32_t getCpuFrequency();
uint32_t getApbFrequency();

unsigned long micros();
unsigned long millis();
void delay(uint32_t);
Expand Down