diff --git a/configure.ac b/configure.ac index 1fa5efc..9d23272 100644 --- a/configure.ac +++ b/configure.ac @@ -50,6 +50,7 @@ AC_CONFIG_FILES([ examples/1rmtmusic/Makefile examples/1rmttm1637/Makefile examples/1rmtws2812/Makefile + examples/2cpuclock/Makefile examples/3prog1/Makefile ld/Makefile ]) diff --git a/examples/2cpuclock/Makefile.am b/examples/2cpuclock/Makefile.am new file mode 100644 index 0000000..23a5672 --- /dev/null +++ b/examples/2cpuclock/Makefile.am @@ -0,0 +1,38 @@ +include $(top_srcdir)/scripts/elf2bin.mk +include $(top_srcdir)/ld/flags.mk + +noinst_HEADERS = defines.h + +if WITH_BINARIES +AM_LDFLAGS += \ + -T $(top_srcdir)/ld/esp32.rom.ld \ + -T $(top_srcdir)/ld/esp32.rom.api.ld \ + -T $(top_srcdir)/ld/esp32.rom.libgcc.ld \ + -T $(top_srcdir)/ld/esp32.rom.newlib-data.ld \ + -T $(top_srcdir)/ld/esp32.rom.newlib-locale.ld \ + -T $(top_srcdir)/ld/esp32.rom.newlib-nano.ld \ + -T $(top_srcdir)/ld/esp32.rom.newlib-time.ld \ + -T $(top_srcdir)/ld/esp32.rom.redefined.ld \ + -T $(top_srcdir)/ld/esp32.rom.syscalls.ld +else +AM_LDFLAGS += \ + -T $(top_srcdir)/ld/esp32.rom.ld \ + -T $(top_srcdir)/ld/esp32.rom.api.ld \ + -T $(top_srcdir)/ld/esp32.rom.libgcc.ld \ + -T $(top_srcdir)/ld/esp32.rom.redefined.ld \ + -T $(top_srcdir)/ld/esp32.rom.syscalls.ld +endif + +AM_CFLAGS = -std=c11 -flto +AM_CPPFLAGS = -I$(top_srcdir)/src -I$(srcdir) +LDADD = $(top_builddir)/src/libesp32basic.a + +bin_PROGRAMS = \ + cpuclock.elf + +if WITH_BINARIES +CLEANFILES = \ + cpuclock.bin +endif + +BUILT_SOURCES = $(CLEANFILES) diff --git a/examples/2cpuclock/README.md b/examples/2cpuclock/README.md new file mode 100644 index 0000000..976895c --- /dev/null +++ b/examples/2cpuclock/README.md @@ -0,0 +1,48 @@ +### Example setting CPU clock source and related things + +CPU clock can have the following sources: + +* `XTL_CLK` +* `PLL_CLK` +* `RC_FAST_CLK` +* `APLL_CLK` + +Currently we support `XTL_CLK` and `PLL_CLK` with the following frequencies: + +* `XTL_CLK` + * 40 MHz + * 20 MHz + * 10 MHz + * 8 MHz + * 5 MHz +* `PLL_CLK` + * 80 MHz + * **160 MHz** (default) + * 240 MHz + +In this example you can select the cpu clock source, +set the clock frequency, +tune the digital voltage regulator (dig_dbias) and +select the UART0 baud rate (115200, 57600, or 9600). + +The NodeMCU onboard LED (GPIO2) blinks at 1 Hz rate based on APB clock. +This feature is useful to see wether the APB clock frequency matches the +expected value. + +#### Control + +* `p` (OK) select `PLL_CLK` as clock source; +* `x` (OK) select `XTL_CLK` as clock source; +* `]` (OK) set higher PLL-based clock frequency; +* `[` (OK) set lower PLL-based clock frequency; +* `-` (OK) set higher XTL_CLK divisor (lower clock frequency); +* `+` (OK) set lower XTL_CLK divisor (higher clock frequency); +* `<` (OK) set lower dig vreg dbias; +* `>` (OK) set higher dig vreg dbias; +* `u` (OK) set lower UART0 baud; +* `U` (OK) set higher UART0 baud; +* `a` (OK) select UART0 clock source (`APB_CLK` or `REF_TICK`); +* `i` (OK) Information about registers; +* `I` (OK) display current settings; +* `r` (OK) when re-entering a CPU freq state, use its last known vreg dbias setting; +* `R` (OK) always use the default vreg dbias setting for any CPU freq state. diff --git a/examples/2cpuclock/cpuclock.c b/examples/2cpuclock/cpuclock.c new file mode 100644 index 0000000..6f0e9b9 --- /dev/null +++ b/examples/2cpuclock/cpuclock.c @@ -0,0 +1,365 @@ +/* + * Copyright 2024 - 2026 SZIGETI János + * + * This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3. + * See LICENSE or for full license details. + */ + +#include +#include +#include +#include +#include + +#include "clock.h" +#include "esp_attr.h" +#include "dport.h" +#include "gpio.h" +#include "iomux.h" +#include "main.h" +#include "defines.h" +#include "romfunctions.h" +#include "rtc.h" +#include "typeaux.h" +#include "uart.h" +#include "utils/uartutils.h" +#include "timg.h" + +// =================== Hard constants ================= +// #0: Timings +#define UART_PERIOD_MS 100U ///< for polling UART0 RX for incoming commands +#define LED_PERIOD_MS 1000U +#define LED_HIGH_MS 100U + +#define REF_TICK_FREQ_HZ 1000000U +#define XTL_CLK_FREQ_HZ 40000000U +// derived +#define XTL_CLK_FREQ_MHZ (XTL_CLK_FREQ_HZ / 1000000U) +#define APB_FREQ_MHZ (APB_FREQ_HZ / 1000000U) +#define TIMG00_XTLBASE_FREQ_KHZ (XTL_CLK_FREQ_HZ / (TIM0_0_DIVISOR * 1000)) + +// #1: Limits + +// #2: Connections +#define LED_GPIO 2U + +// #3: Sizes + +// ============= Local types =============== + + +// ================ Local function declarations ================= +static uint32_t _ms_to_tick(uint32_t u32msValue); +static void _led_init(); +static void _led_cycle(uint64_t u64tckNow); +static void _switch_to_any_clk(ECpuClockSource eClk); +static void _uart0_autoconf(); +static void _uart_config(bool bApbBased, bool bPllCpuClkSrc, uint32_t u32Baud); +static void _uart_init(); +static void _uart_cycle(uint64_t u64tckNow); +static void _vregdbias_init(); +static uint8_t _xtal_ref_tick_div(uint8_t u8Idx); + +// =================== Global constants ================ +const bool gbStartAppCpu = START_APP_CPU; +const uint16_t gu16Tim00Divisor = TIM0_0_DIVISOR; +const uint64_t gu64tckSchedulePeriod = (CLK_FREQ_HZ / SCHEDULE_FREQ_HZ); + +// ==================== Local Data ================ +/// Predefined set of XTL_CLK divisors +const static uint32_t gau32XtalDiv[] = { + 1, 2, 4, 5, 8, 10 +}; +static uint8_t gu8XtalClkIdx = 0; +static EPllCpuClockFreq gePllCpuClkFreq = PLLCPUFREQ_160MHZ; +static ECpuClockSource geCpuClkSrc = CLK_PLL; + +/// Predefined UART baud rates +const static uint32_t gau32UartBaud[] = { + 9600, 19200, 38400, 57600, 115200, 230400, 460800 +}; +static uint8_t gu8UartBaudIdx = 4; + +/// Possible CPU voltage levels (mV) +const static uint32_t gau32Vreg[] = { + 900, 950, 1000, 1050, 1100, 1150, 1200, 1250 +}; + +static bool gbUart0ApbBased = true; + +static uint8_t gau8XtalVregDBiasUser[ARRAY_SIZE(gau32XtalDiv)]; +static uint8_t gau8PllVregDBiasUser[3]; +static bool gbLoadVregDBias; + +// ============== Implementation ============== +// -------------- Internal functions -------------- + +static uint32_t _ms_to_tick(uint32_t u32msValue) { + if (geCpuClkSrc == CLK_PLL) return MS2TICKS(u32msValue); + return (TIMG00_XTLBASE_FREQ_KHZ / gau32XtalDiv[gu8XtalClkIdx]) * u32msValue; +} + +static void _led_init() { + gpio_pin_enable(LED_GPIO); +} + +static void _led_cycle(uint64_t u64tckNow) { + static const RegAddr aprGpioOut[] = {&gsGPIO.OUT_W1TC, &gsGPIO.OUT_W1TS}; + static uint64_t u64tckNext = 0; + static bool gbLedOn = false; + + if (u64tckNext <= u64tckNow) { + gbLedOn = !gbLedOn; + gpio_reg_setbit(aprGpioOut[gbLedOn], LED_GPIO); + u64tckNext += _ms_to_tick(gbLedOn ? LED_HIGH_MS : (LED_PERIOD_MS - LED_HIGH_MS)); + } +} + +static void _uart_config(bool bApbBased, bool bPllCpuClkSrc, uint32_t u32Baud) { + gsUART0.CONF0 = FIELD_REPLACE(gsUART0.CONF0, bApbBased ? 1 : 0, UART_TICK_REF_ALWAYS_ON); + uint32_t u32ClkFreqHz = bApbBased ? (bPllCpuClkSrc ? APB_FREQ_HZ : XTL_CLK_FREQ_HZ / gau32XtalDiv[gu8XtalClkIdx]) : REF_TICK_FREQ_HZ; + gsUART0.CLKDIV.raw = UART_HZ2CLKDIV(u32Baud, u32ClkFreqHz); +} + +static void _uart0_autoconf() { + _uart_config(gbUart0ApbBased, geCpuClkSrc == CLK_PLL, gau32UartBaud[gu8UartBaudIdx]); +} + +static void _uart_init() { + _uart0_autoconf(); +} + +static void _vregdbias_init() { + for (int i = 0; i < ARRAY_SIZE(gau8PllVregDBiasUser); ++i) { + gau8PllVregDBiasUser[i] = -1; // not set yet + } + for (int i = 0; i < ARRAY_SIZE(gau8XtalVregDBiasUser); ++i) { + gau8XtalVregDBiasUser[i] = -1; // not set yet + } + gbLoadVregDBias = true; +} + +static uint8_t _xtal_ref_tick_div(uint8_t u8Idx) { + return (XTL_CLK_FREQ_HZ / REF_TICK_FREQ_HZ) / gau32XtalDiv[u8Idx]; +}; + +static inline void _print_xtl_params() { + uint32_t u32CpuFreqKHz = (XTL_CLK_FREQ_HZ / 1000) / gau32XtalDiv[gu8XtalClkIdx]; + uint32_t u32RefFreqKHz = u32CpuFreqKHz / _xtal_ref_tick_div(gu8XtalClkIdx); + uart_printf(&gsUART0, "XTAL div#%u: %u, %u: %uKHz (ref: %uKHz)\r\n", + gu8XtalClkIdx, gau32XtalDiv[gu8XtalClkIdx], _xtal_ref_tick_div(gu8XtalClkIdx), + u32CpuFreqKHz, u32RefFreqKHz); +} + +static bool _modify_idx(uint8_t *pu8Idx, int8_t i8Diff, uint8_t u8Size, const uint32_t *pu32Values, const char *strText) { + bool bRet = false; + uint8_t u8IdxOrig = *pu8Idx; + if (i8Diff < 0) { + if (-i8Diff < *pu8Idx) { + *pu8Idx += i8Diff; + } else { + *pu8Idx = 0; + } + } else { + if (*pu8Idx + i8Diff < u8Size) { + *pu8Idx += i8Diff; + } else { + *pu8Idx = u8Size - 1; + } + } + if (*pu8Idx != u8IdxOrig) { + if (pu32Values) { + uart_printf(&gsUART0, "%s changed: %u -> %u\r\n", strText, pu32Values[u8IdxOrig], pu32Values[*pu8Idx]); + } else { + uart_printf(&gsUART0, "%s idx changed: %u -> %u\r\n", strText, u8IdxOrig, *pu8Idx); + } + bRet = true; + } else { + if (pu32Values) { + uart_printf(&gsUART0, "%s (%u) not changed\r\n", strText, pu32Values[u8IdxOrig]); + } else { + uart_printf(&gsUART0, "%s idx (%u) not changed\r\n", strText, u8IdxOrig); + } + } + return bRet; +} + +static void _switch_to_any_clk(ECpuClockSource eClk) { + ECpuClockSource eCpuClkSrcOrig = geCpuClkSrc; + if (eClk == CLK_XTL) { + clock_switch_to_xtl(gau32XtalDiv[gu8XtalClkIdx], _xtal_ref_tick_div(gu8XtalClkIdx)); + } else if (eClk == CLK_PLL) { + clock_switch_to_pll(gePllCpuClkFreq); + } + geCpuClkSrc = eClk; + uint8_t u8FreqIdx = ((eClk == CLK_XTL) ? gu8XtalClkIdx : gePllCpuClkFreq); + uint8_t *pu8VregDBiasUser = ((eClk == CLK_XTL) ? gau8XtalVregDBiasUser : gau8PllVregDBiasUser); + + if (gbLoadVregDBias && pu8VregDBiasUser[u8FreqIdx] < 8) { + clock_set_dig_vreg_dbias_wak(pu8VregDBiasUser[u8FreqIdx]); + uart_printf(&gsUART0, "Dig Vreg DBias set to %u mV\r\n", gau32Vreg[pu8VregDBiasUser[u8FreqIdx]]); + } + // resolve UART baud issue in case of APB freq change + if (gbUart0ApbBased && !(eCpuClkSrcOrig == CLK_PLL && eClk == CLK_PLL)) { + _uart0_autoconf(); + } +} + +static void _uart_cycle(uint64_t u64tckNow) { + static uint64_t u64tckNext = MS2TICKS(1000); + + if (u64tckNext <= u64tckNow) { + while (0 < (gsUART0.STATUS & 0xff)) { + char cCtrl = gsUART0Mapped.FIFO & 0xff; + int8_t i8UartBaudDiff = 0; + int8_t i8XtlDivDiff = 0; + int8_t i8PllFreqSelDiff = 0; + int8_t i8DigVregDBiasDiff = 0; + + switch (cCtrl) { + case '-': + --i8XtlDivDiff; + break; + case '+': + ++i8XtlDivDiff; + break; + + case 'r': + gbLoadVregDBias = true; + uart_printf(&gsUART0, "Load stored vreg dbias when CPU CLK is changed.\r\n"); + break; + case 'R': + gbLoadVregDBias = false; + uart_printf(&gsUART0, "Load default vreg dbias when CPU CLK is changed.\r\n"); + break; + + // modify UART0 baud + case 'u': + --i8UartBaudDiff; + break; + case 'U': + ++i8UartBaudDiff; + break; + // modify PLL-based CPU freq + case '[': + --i8PllFreqSelDiff; + break; + case ']': + ++i8PllFreqSelDiff; + break; + // toggle UART0 clock source + case 'a': + gbUart0ApbBased = !gbUart0ApbBased; + _uart0_autoconf(); + uart_printf(&gsUART0, "UART0 based on %s\r\n", gbUart0ApbBased ? "APB_CLK" : "REF_TICK"); + break; + // print register information + case 'i': + uart_printf(&gsUART0, "UART\tCONF0: %08X\tAUTOBAUD: %08X\tCLKDIV: %08X\r\n", gsUART0.CONF0, gsUART0.AUTOBAUD, gsUART0.CLKDIV); + uart_printf(&gsUART0, "SYSCON\tSYSCLK: %08X\tXTALTICK: %08X\tPLLTICK: %08X\r\n", gprSYSCON[0], gprSYSCON[1], gprSYSCON[2]); + break; + case 'I': + uart_printf(&gsUART0, "ClkSrc: %u, XTL freq: #%u (%u MHz), PLL freq: #%u (%u MHz), UART_APB: %c, LoadDBias: %c, dbias: #%u\r\n", + geCpuClkSrc, + gu8XtalClkIdx, XTL_CLK_FREQ_MHZ / gau32XtalDiv[gu8XtalClkIdx], + gePllCpuClkFreq, APB_FREQ_MHZ * (1 + gePllCpuClkFreq), + gbUart0ApbBased ? 'Y' : 'N', gbLoadVregDBias ? 'Y' : 'N', clock_get_dig_vreg_dbias_wak()); + break; + case 'x': // switch to xtal + { + uart_printf(&gsUART0, "Switch to XTL CPU clock source..."); + _switch_to_any_clk(CLK_XTL); + uart_printf(&gsUART0, "done.\r\n"); + } + break; + case 'p': // switch to xtal + { + uart_printf(&gsUART0, "Switch to PLL CPU clock source..."); + _switch_to_any_clk(CLK_PLL); + uart_printf(&gsUART0, "done.\r\n"); + } + break; + + case '<': + --i8DigVregDBiasDiff; + break; + case '>': + ++i8DigVregDBiasDiff; + break; + default: + uart_printf(&gsUART0, "_ _ _ [%c] _ _ _\r\n", cCtrl); + } + + // change uart0 baud + if (i8UartBaudDiff != 0) { + if (_modify_idx(&gu8UartBaudIdx, i8UartBaudDiff, ARRAY_SIZE(gau32UartBaud), gau32UartBaud, "UART0 baud")) { + _uart0_autoconf(); + } + } + + // set XTL divisor + if (i8XtlDivDiff != 0) { + if (_modify_idx(&gu8XtalClkIdx, i8XtlDivDiff, ARRAY_SIZE(gau32XtalDiv), gau32XtalDiv, "XTAL DIV")) { + if (geCpuClkSrc == CLK_XTL) { + _switch_to_any_clk(CLK_XTL); + } else { + uart_printf(&gsUART0, "But CPU clock not changed as current clock source is not XTAL.\r\n"); + } + } + } + + // set PLL frequency + if (i8PllFreqSelDiff != 0) { + uint8_t u8PllFreqSel = gePllCpuClkFreq; + if (_modify_idx(&u8PllFreqSel, i8PllFreqSelDiff, 3, NULL, "PLL Freq. selector")) { + gePllCpuClkFreq = u8PllFreqSel; + if (geCpuClkSrc == CLK_PLL) { + _switch_to_any_clk(CLK_PLL); + } else { + uart_printf(&gsUART0, "But CPU clock not changed as current clock source is not PLL.\r\n"); + } + } + } + + // set digital voltage regulator dbias + if (i8DigVregDBiasDiff != 0) { + uint8_t u8dbias = clock_get_dig_vreg_dbias_wak(); + if (_modify_idx(&u8dbias, i8DigVregDBiasDiff, ARRAY_SIZE(gau32Vreg), gau32Vreg, "dig vreg dbias [mV]")) { + clock_set_dig_vreg_dbias_wak(u8dbias); + uart_printf(&gsUART0, "DBIAS: %u\r\n", clock_get_dig_vreg_dbias_wak()); + } else { + uart_printf(&gsUART0, "Dig vreg dbias not modified\r\n"); + } + if (geCpuClkSrc == CLK_XTL) { + gau8XtalVregDBiasUser[gu8XtalClkIdx] = u8dbias; + } else if (geCpuClkSrc == CLK_PLL) { + gau8PllVregDBiasUser[gePllCpuClkFreq] = u8dbias; + } + } + } + + u64tckNext += _ms_to_tick(UART_PERIOD_MS); + } +} + +// -------------- Interface functions -------------- + +void prog_init_pro_pre() { + _led_init(); + _uart_init(); + _vregdbias_init(); +} + +void prog_init_app() { +} + +void prog_init_pro_post() { +} + +void prog_cycle_app(uint64_t u64tckNow) { +} + +void prog_cycle_pro(uint64_t u64tckNow) { + _led_cycle(u64tckNow); + _uart_cycle(u64tckNow); +} diff --git a/examples/2cpuclock/defines.h b/examples/2cpuclock/defines.h new file mode 100644 index 0000000..d61d4d7 --- /dev/null +++ b/examples/2cpuclock/defines.h @@ -0,0 +1,36 @@ +/* + * Copyright 2024 SZIGETI János + * + * This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3. + * See LICENSE or for full license details. + */ +#ifndef DEFINES_H +#define DEFINES_H + +#ifdef __cplusplus +extern "C" { +#endif + + // TIMINGS + // const -- do not change this value +#define APB_FREQ_HZ 80000000U // 80 MHz + + // variables +#define TIM0_0_DIVISOR 2U +#define START_APP_CPU 0U +#define SCHEDULE_FREQ_HZ 1000U // 10KHz + + // derived invariants +#define CLK_FREQ_HZ (APB_FREQ_HZ / TIM0_0_DIVISOR) // 40 MHz +#define TICKS_PER_MS (CLK_FREQ_HZ / 1000U) // 40000 +#define TICKS_PER_US (CLK_FREQ_HZ / 1000000U) // 40 + +#define MS2TICKS(X) ((X) * TICKS_PER_MS) +#define HZ2APBTICKS(X) (APB_FREQ_HZ / (X)) + +#ifdef __cplusplus +} +#endif + +#endif /* DEFINES_H */ + diff --git a/examples/Makefile.am b/examples/Makefile.am index 30fed6c..949b423 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,4 +1,4 @@ AUTOMAKE_OPTIONS = SUBDIRS=0blink 0button 0hello 0ledctrl 0timerint 0timgalarm 0uart 0udma \ 1bme280 1i2cssd1306 1rmtblink 1rmtdht 1rmtmorse 1rmtmusic 1rmttm1637 \ -3prog1 1rmtws2812 +2cpuclock 3prog1 1rmtws2812 diff --git a/ld/esp32peripheral.ld b/ld/esp32peripheral.ld index a453233..5d8d7d0 100644 --- a/ld/esp32peripheral.ld +++ b/ld/esp32peripheral.ld @@ -1,20 +1,22 @@ -PROVIDE ( gsGPIO = 0x3ff44000 ); -PROVIDE ( grIOMUX = 0x3ff49000 ); -PROVIDE ( gsDPORT = 0x3ff00000); +PROVIDE ( gsDPORT = 0x3ff00000); PROVIDE ( gsPIDCTRL = 0x3ff1f000); -PROVIDE ( gsRTC = 0x3ff48000); -PROVIDE ( gsRMT = 0x3ff56000 ); -PROVIDE ( grRMTRAM = 0x3ff56800 ); -PROVIDE ( gsTIMG0 = 0x3ff5f000); -PROVIDE ( gsTIMG1 = 0x3ff60000); -PROVIDE ( gsI2C0 = 0x3ff53000); -PROVIDE ( gsI2C1 = 0x3ff67000); -PROVIDE ( gsUART0 = 0x60000000); -PROVIDE ( gsUART1 = 0x60010000); -PROVIDE ( gsUART2 = 0x602E0000); +PROVIDE ( gsGPIO = 0x3ff44000); +PROVIDE ( gsRTC = 0x3ff48000); +PROVIDE ( grIOMUX = 0x3ff49000); +PROVIDE ( gsI2C0 = 0x3ff53000); +PROVIDE ( gsRMT = 0x3ff56000); +PROVIDE ( grRMTRAM = 0x3ff56800); +PROVIDE ( gsEFUSE = 0x3FF5A000); +PROVIDE ( gsTIMG0 = 0x3ff5f000); +PROVIDE ( gsTIMG1 = 0x3ff60000); +PROVIDE ( gprSYSCON = 0x3FF66000); +PROVIDE ( gsI2C1 = 0x3ff67000); +PROVIDE ( gsUART0 = 0x60000000); +PROVIDE ( gsUART1 = 0x60010000); +PROVIDE ( gsUART2 = 0x602E0000); PROVIDE ( gsUART0Mapped = 0x3ff40000); PROVIDE ( gsUART1Mapped = 0x3ff50000); PROVIDE ( gsUART2Mapped = 0x3ff6e000); -PROVIDE ( gsUHCI0 = 0x3FF54000); -PROVIDE ( gsUHCI1 = 0x3FF4C000); +PROVIDE ( gsUHCI0 = 0x3FF54000); +PROVIDE ( gsUHCI1 = 0x3FF4C000); PROVIDE ( _impure_ptr = 0x3ffae0b0); diff --git a/src/Makefile.am b/src/Makefile.am index ba8e99b..2b1ea6c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,13 +4,13 @@ libesp32basic_a_AR=$(AR) rcs lib_LIBRARIES = libesp32basic.a -nobase_include_HEADERS = dport.h e32b-config.h esp32types.h esp_attr.h gpio.h i2c.h \ +nobase_include_HEADERS = clock.h dport.h e32b-config.h efuse.h esp32types.h esp_attr.h gpio.h i2c.h \ iomux.h lockmgr.h main.h pidctrl.h print.h rmt.h romfunctions.h rtc.h timg.h \ typeaux.h uart.h xtutils.h \ utils/crc.h utils/i2cutils.h utils/i2ciface.h utils/rmtutils.h utils/uartutils.h utils/generators.h nodist_include_HEADERS = -libesp32basic_a_SOURCES = i2c.c lockmgr.c main.c rmt.c timg.c uart.c utils/crc.c utils/i2cutils.c utils/rmtutils.c utils/uartutils.c utils/generators.c +libesp32basic_a_SOURCES = clock.c i2c.c lockmgr.c main.c rmt.c timg.c uart.c utils/crc.c utils/i2cutils.c utils/rmtutils.c utils/uartutils.c utils/generators.c nodist_libesp32basic_a_SOURCES = CLEANFILES = diff --git a/src/clock.c b/src/clock.c new file mode 100644 index 0000000..f57d4b8 --- /dev/null +++ b/src/clock.c @@ -0,0 +1,175 @@ +/* + * Copyright 2026 SZIGETI János + * + * This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3. + * See LICENSE or for full license details. + */ + +#include +#include + +#include "clock.h" +#include "efuse.h" +#include "typeaux.h" +#include "xtutils.h" +#include "rtc.h" +#include "dport.h" +#include "romfunctions.h" + +// ============== Defines ============== +#define SYSCON_PRE_DIV_CNT 0 +#define SYSCON_PRE_DIV_CNT_OFS 0U +#define SYSCON_PRE_DIV_CNT_BITS 10U + +#define SYSCON_XTAL_TICK_NUM 0 +#define SYSCON_XTAL_TICK_NUM_OFS 0 +#define SYSCON_XTAL_TICK_NUM_BITS 8 + +#define RTC_CNTL_SOC_CLK_SEL 0 // use as PFX +#define RTC_CNTL_SOC_CLK_SEL_OFS 27U +#define RTC_CNTL_SOC_CLK_SEL_BITS 2U + +#define DPORT_CPU_CPUPERIOD_SEL 0 // use as PFX +#define DPORT_CPU_CPUPERIOD_SEL_OFS 0U +#define DPORT_CPU_CPUPERIOD_SEL_BITS 2U + +#define RTC_CNTL_DIG_VREG_DBIAS_WAK 0 // use as PFX +#define RTC_CNTL_DIG_VREG_DBIAS_WAK_OFS 11 +#define RTC_CNTL_DIG_VREG_DBIAS_WAK_BITS 3 + +#define DBIAS_1V00 2U +#define DBIAS_1V10 4U +#define DBIAS_1V25 7U + +#define I2C_BBPLL_ADDR_LREF 2 // div_ref, div10_8, lref +#define I2C_BBPLL_ADDR_DIV7_0 3 // div7_0 +#define I2C_BBPLL_ADDR_DCUR 5 // dcur, bw +#define I2C_BBPLL_ADDR_ENDIV5 11 +#define I2C_BBPLL_ADDR_BBADC_DSNP 9 + +#define I2CBBPLL_DIVREF_ADDR I2C_BBPLL_ADDR_LREF +#define I2CBBPLL_DIVREF_OFS 0 +#define I2CBBPLL_DIVREF_BITS 4 +#define I2CBBPLL_DIV7_0_ADDR I2C_BBPLL_ADDR_DIV7_0 +#define I2CBBPLL_DIV7_0_OFS 0 +#define I2CBBPLL_DIV7_0_BITS 8 +#define I2CBBPLL_DIV10_8_ADDR I2C_BBPLL_ADDR_LREF +#define I2CBBPLL_DIV10_8_OFS 4 +#define I2CBBPLL_DIV10_8_BITS 3 +#define I2CBBPLL_LREF_ADDR I2C_BBPLL_ADDR_LREF +#define I2CBBPLL_LREF_OFS 7 +#define I2CBBPLL_LREF_BITS 1 +#define I2CBBPLL_DCUR_ADDR I2C_BBPLL_ADDR_DCUR +#define I2CBBPLL_DCUR_OFS 0 +#define I2CBBPLL_DCUR_BITS 3 +#define I2CBBPLL_BW_ADDR I2C_BBPLL_ADDR_DCUR +#define I2CBBPLL_BW_OFS 6 +#define I2CBBPLL_BW_BITS 2 + +// ============== Local types ============== + +typedef struct { + uint8_t divRef; // 4bits + uint8_t div7_0; + uint8_t div10_8; + uint8_t lref; + uint8_t dcur; + uint8_t bw; + uint8_t endiv5; + uint8_t bbadcDsmpVal; +} SPllValues; + +// ============== Local module-wide variables ============== +static const SPllValues gasBbPllValues[] = { + {0, 32, 0, 0, 6, 3, 0x43, 0x84}, // 320/160MHz + {0, 28, 0, 0, 6, 3, 0xC3, 0x74} // 480MHz +}; + +// ============== Internal function declarations ============== +// efuse register should be read only once (as it does not change its value) + +static uint8_t _dbias240mhz() { + static bool bFirst = true; + static uint8_t u8Ret; + if (bFirst) { + bFirst = false; + u8Ret = DBIAS_1V25 - FIELD_GET(gsEFUSE.BLK0RDATA[EFUSE_VOL_LVL_HP_INV_BLK0REG], EFUSE_VOL_LVL_HP_INV); + } + return u8Ret; +} +// ============== Implementation ============== +// -------------- Internal functions -------------- + +// -------------- Interface functions -------------- + +uint8_t clock_get_dig_vreg_dbias_wak() { + return FIELD_GET(gsRTC.VREG, RTC_CNTL_DIG_VREG_DBIAS_WAK); +} + +void clock_set_dig_vreg_dbias_wak(uint8_t u8DigDbias) { + gsRTC.VREG = FIELD_REPLACE(gsRTC.VREG, u8DigDbias, RTC_CNTL_DIG_VREG_DBIAS_WAK); +} + +ECpuClockSource clock_get_cpuclksrc() { + return FIELD_GET(gsRTC.CLK_CONF, RTC_CNTL_SOC_CLK_SEL); +} + +EPllCpuClockFreq clock_get_pllcpufreq() { + return FIELD_GET(gsDPORT.CPU_PER_CONF, DPORT_CPU_CPUPERIOD_SEL); +}; + +void clock_switch_to_xtl(uint16_t u16Divisor, uint8_t u8RefDivisor) { + gprSYSCON[0] = FIELD_REPLACE(gprSYSCON[0], u16Divisor - 1, SYSCON_PRE_DIV_CNT); + gprSYSCON[1] = FIELD_REPLACE(gprSYSCON[1], u8RefDivisor - 1, SYSCON_XTAL_TICK_NUM); + gsRTC.CLK_CONF = FIELD_REPLACE(gsRTC.CLK_CONF, CLK_XTL, RTC_CNTL_SOC_CLK_SEL); + uint8_t u8DigDBias = (1 < u16Divisor) ? DBIAS_1V00 : DBIAS_1V10; + clock_set_dig_vreg_dbias_wak(u8DigDBias); +} + +void clock_switch_to_pll(EPllCpuClockFreq eFreq) { + // get current clk src + ECpuClockSource eClkSrc = clock_get_cpuclksrc(); + + // set XTAL as clk src + if (eClkSrc != CLK_XTL) { + gsRTC.CLK_CONF = FIELD_REPLACE(gsRTC.CLK_CONF, CLK_XTL, RTC_CNTL_SOC_CLK_SEL); + esp_rom_delay_us(1); + } + + // set voltage + { + uint8_t u8DigDBias = (eFreq < PLLCPUFREQ_240MHZ) ? DBIAS_1V10 : _dbias240mhz(); + clock_set_dig_vreg_dbias_wak(u8DigDBias); + } + + esp_rom_delay_us(1); + + // bbpll configuration + { + const SPllValues *psVals = &gasBbPllValues[(eFreq < PLLCPUFREQ_240MHZ) ? 0 : 1]; + // div_ref, div10_8, lref + uint8_t lref = FIELD_MASKNSHIFT(psVals->divRef, I2CBBPLL_DIVREF); + lref |= FIELD_MASKNSHIFT(psVals->div10_8, I2CBBPLL_DIV10_8); + lref |= FIELD_MASKNSHIFT(psVals->lref, I2CBBPLL_LREF); + esp_rom_regi2c_write(0x66, 4, I2C_BBPLL_ADDR_LREF, lref); + + uint8_t div7_0 = FIELD_MASKNSHIFT(psVals->div7_0, I2CBBPLL_DIV7_0); + esp_rom_regi2c_write(0x66, 4, I2C_BBPLL_ADDR_DIV7_0, div7_0); + + uint8_t dcur = FIELD_MASKNSHIFT(psVals->dcur, I2CBBPLL_DCUR); + dcur |= FIELD_MASKNSHIFT(psVals->bw, I2CBBPLL_BW); + esp_rom_regi2c_write(0x66, 4, I2C_BBPLL_ADDR_DCUR, dcur); + + esp_rom_regi2c_write(0x66, 4, I2C_BBPLL_ADDR_ENDIV5, psVals->endiv5); + esp_rom_regi2c_write(0x66, 4, I2C_BBPLL_ADDR_BBADC_DSNP, psVals->bbadcDsmpVal); + } + esp_rom_delay_us(1); + + // set frequency + gsDPORT.CPU_PER_CONF = FIELD_REPLACE(gsDPORT.CPU_PER_CONF, eFreq, DPORT_CPU_CPUPERIOD_SEL); + + esp_rom_delay_us(1); + + // set PLL as clk src + gsRTC.CLK_CONF = FIELD_REPLACE(gsRTC.CLK_CONF, CLK_PLL, RTC_CNTL_SOC_CLK_SEL); +} diff --git a/src/clock.h b/src/clock.h new file mode 100644 index 0000000..7ae7453 --- /dev/null +++ b/src/clock.h @@ -0,0 +1,61 @@ +/* + * Copyright 2026 SZIGETI János + * + * This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3. + * See LICENSE or for full license details. + */ + +#ifndef CLOCK_H +#define CLOCK_H + +#include "esp32types.h" + +/** + * @file + * Clock-related types and functions. + * Note, not to confuse with General-purpose Timers, @ref timg.h "TIMG"! + * @see timg.h + */ +#ifdef __cplusplus +extern "C" { +#endif + + // ============== Defines ============== + + + // ============== Types ============== + + typedef enum { + CLK_XTL = 0, + CLK_PLL, + CLK_RC_FAST, + CLK_APLL + } ECpuClockSource; ///< Clock source of the CPU. + + typedef enum { + PLLCPUFREQ_80MHZ = 0, + PLLCPUFREQ_160MHZ, + PLLCPUFREQ_240MHZ, + PLLCPUFREQ_INVALID + } EPllCpuClockFreq; ///< PLL based CPU clock frequencies. + + + // ============== Global values / References ============== + extern Reg gprSYSCON[32]; + + // ============== Inline interface functions ============== + + // ============== Interface functions ============== + uint8_t clock_get_dig_vreg_dbias_wak(); + void clock_set_dig_vreg_dbias_wak(uint8_t u8DigDbias); + ECpuClockSource clock_get_cpuclksrc(); + EPllCpuClockFreq clock_get_pllcpufreq(); + void clock_switch_to_xtl(uint16_t u16Divisor, uint8_t u8RefDivisor); + void clock_switch_to_pll(EPllCpuClockFreq eFreq); + +#ifdef __cplusplus +} +#endif + +#endif /* CLOCK_H */ + diff --git a/src/efuse.h b/src/efuse.h new file mode 100644 index 0000000..d8feb4b --- /dev/null +++ b/src/efuse.h @@ -0,0 +1,62 @@ +/* + * Copyright 2026 SZIGETI János + * + * This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3. + * See LICENSE or for full license details. + */ + +#ifndef EFUSE_H +#define EFUSE_H + +#ifdef __cplusplus +extern "C" { +#endif +#include "esp32types.h" + + // ============== Defines ============== +#define EFUSE_VOL_LVL_HP_INV 0 // use as PFX +#define EFUSE_VOL_LVL_HP_INV_BLK0REG 5U +#define EFUSE_VOL_LVL_HP_INV_OFS 22U +#define EFUSE_VOL_LVL_HP_INV_BITS 2U + +#define EFUSE_DIG_VOL_L6 0 // use as PFX +#define EFUSE_DIG_VOL_L6_BLK0REG 5U +#define EFUSE_DIG_VOL_L6_OFS 24U +#define EFUSE_DIG_VOL_L6_BITS 4 + + // ============== Types ============== + typedef struct { + Reg BLK0RDATA[7]; + Reg rsvd_x20[5]; + Reg BLK1RDATA[8]; + Reg BLK2RDATA[8]; + Reg BLK3RDATA[8]; + Reg rsvd_x98[0x60]; + Reg CLK; + Reg CONF; + Reg CMD; + Reg INT_RAW; + Reg INT_ST; + Reg INT_ENA; + Reg INT_CLR; + Reg DAC_CONF; + Reg DEC_STATUS; + } EFUSE_Type; + + // ============== Global values / References ============== + extern EFUSE_Type gsEFUSE; + + // ============== Inline interface functions ============== + + // ============== Interface functions ============== + + + + + +#ifdef __cplusplus +} +#endif + +#endif /* EFUSE_H */ + diff --git a/src/esp32types.h b/src/esp32types.h index f3bfa6a..392964e 100644 --- a/src/esp32types.h +++ b/src/esp32types.h @@ -14,6 +14,16 @@ extern "C" { #include // ============== Defines ============== +/// Transforms number of bits into bitmask, e.g., BITS2MASK(3): 7, BITS2MASK(5): 31 +#define BITS2MASK(X) ( ( 1 << ( X ) ) - 1) +/// Get a field (shifted to LSB) form a (register) value, e.g., with FOO_BITS=3 and FOO_OFS=2, FIELD_GET(0x2C, FOO): 3 (bits2..4) +#define FIELD_GET(VAL,FIELD) ( ( (VAL) >> FIELD##_OFS ) & BITS2MASK( FIELD##_BITS ) ) +/// Get the bitmask of a given field. +#define FIELD_MASK(PFX) ( BITS2MASK( PFX##_BITS ) << PFX##_OFS ) +/// Take a field value (X), first crop it to the given bitsize, then shift it to its position (offset). +#define FIELD_MASKNSHIFT(X,PFX) ( ( X ) & BITS2MASK( PFX##_BITS ) << PFX##_OFS ) +/// Take a register value (VAL), and replace its field (PFX) with a given value (X). +#define FIELD_REPLACE(VAL,X,PFX) ( ( ( VAL ) & ~( BITS2MASK( PFX##_BITS ) << PFX##_OFS ) ) | ( ( ( X ) & BITS2MASK( PFX##_BITS )) << PFX##_OFS ) ) // ============== Types ============== diff --git a/src/romfunctions.h b/src/romfunctions.h index 4e7dfb4..a145ed8 100644 --- a/src/romfunctions.h +++ b/src/romfunctions.h @@ -24,6 +24,7 @@ extern "C" { void ets_isr_mask(uint32_t mask); void ets_isr_unmask(uint32_t mask); + void esp_rom_delay_us(uint32_t u32Delay); void *_xtos_set_interrupt_handler(int irq_number, void* function); void *_xtos_set_interrupt_handler_arg(int irq_number, void* function, int argument); @@ -38,6 +39,13 @@ extern "C" { extern uint32_t xthal_get_ccompare(int iReg); void xthal_set_intclear(uint32_t u32IntMask); +// taken from ESP-IDF (v5.5.2) esp_rom_regi2c.h + // (block, hostid) pair is (0x66, 4) for BBPLL and (0x6D, 3) for audio PLL +uint8_t esp_rom_regi2c_read(uint8_t block, uint8_t host_id, uint8_t reg_add); +uint8_t esp_rom_regi2c_read_mask(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t msb, uint8_t lsb); +void esp_rom_regi2c_write(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t data); +void esp_rom_regi2c_write_mask(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t msb, uint8_t lsb, uint8_t data); + #ifdef __cplusplus } #endif diff --git a/src/uart.h b/src/uart.h index 04a9425..6a6e2a1 100644 --- a/src/uart.h +++ b/src/uart.h @@ -20,6 +20,10 @@ extern "C" { // Y: source CLK frequency (APB or REF) #define UART_HZ2CLKDIV(X, Y) UART_TCK2CLKDIV(((Y) << 4) / (X)) +#define UART_TICK_REF_ALWAYS_ON 0 +#define UART_TICK_REF_ALWAYS_ON_OFS 27U +#define UART_TICK_REF_ALWAYS_ON_BITS 1U + // ============== Types ============== typedef enum {