Skip to content

[ESP32-C2] Flash chip speed set to 60 MHz, but SPI clock = 40 #8960

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

Closed
1 task done
TD-er opened this issue Dec 5, 2023 · 11 comments
Closed
1 task done

[ESP32-C2] Flash chip speed set to 60 MHz, but SPI clock = 40 #8960

TD-er opened this issue Dec 5, 2023 · 11 comments

Comments

@TD-er
Copy link
Contributor

TD-er commented Dec 5, 2023

Board

ESP32-C2

Device Description

4MB ESP32-C2

Hardware Configuration

Nothing attached

Version

latest master (checkout manually)

IDE Name

PlatformIO

Operating System

Windows 11

Flash frequency

60MHz

PSRAM enabled

no

Upload speed

115200

Description

Flash speed in the board definition is set to 60 MHz, but reported SPI clock speed is 40 MHz.

This is my code to get the SPI bus clock frequency:

// All ESP32-variants have the SPI flash wired to SPI peripheral 1
  const uint32_t spi_clock = REG_READ(SPI_CLOCK_REG(1));

  addLog(LOG_LEVEL_INFO,   strformat(
    F("SPI_clock: %x  Bit31: %d SPI_CLOCK_REG(1): %x"), 
    spi_clock, spi_clock & BIT(31), SPI_CLOCK_REG(1)));

  if (spi_clock & BIT(31)) {
    // spi_clk is equal to system clock
    return getApbFrequency();
  }
  return spiClockDivToFrequency(spi_clock);

Which returns this log on my (ESPEasy) system:

00:01:49.923 : (86316) Info   : SPI_clock: 7fc  Bit31: 0 SPI_CLOCK_REG(1): 6000200c

As can be seen, bit31 is not set to 0, and thus it is not equal to the system clock.
See:

typedef union {
uint32_t value;
struct {
uint32_t clkcnt_l: 6; /*it must be equal to spi_clkcnt_N.*/
uint32_t clkcnt_h: 6; /*it must be floor((spi_clkcnt_N+1)/2-1).*/
uint32_t clkcnt_n: 6; /*it is the divider of spi_clk. So spi_clk frequency is system/(spi_clkdiv_pre+1)/(spi_clkcnt_N+1)*/
#if CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2
uint32_t clkdiv_pre: 4; /*it is pre-divider of spi_clk.*/
uint32_t reserved: 9; /*reserved*/
#else
uint32_t clkdiv_pre: 13; /*it is pre-divider of spi_clk.*/
#endif
uint32_t clk_equ_sysclk: 1; /*1: spi_clk is eqaul to system 0: spi_clk is divided from system clock.*/
};
} spiClk_t;
#define ClkRegToFreq(reg) (apb_freq / (((reg)->clkdiv_pre + 1) * ((reg)->clkcnt_n + 1)))
uint32_t spiClockDivToFrequency(uint32_t clockDiv)
{
uint32_t apb_freq = getApbFrequency();
spiClk_t reg = { clockDiv };
return ClkRegToFreq(&reg);
}

spi_clock register value of 0x7FC = 011111 111100 and thus only clkcnt_l and clkcnt_h are set, the rest is 0.
Meaning spiClockDivToFrequency() returns just the APB frequency, which is 40 MHz.

I also tested with SPI bus 0:

29.839 : (78896) Info   : SPI_clock: 0  Bit31: 0 SPI_CLOCK_REG(0): 6000300c

Here all bits of spi_clock are 0.

I wonder whether this calculation and/or register interpretation is correct for the C2. (and others?)
Or perhaps the reported APB frequency is incorrect?

See:

static uint32_t calculateApb(rtc_cpu_freq_config_t * conf){
#if CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32H2
return APB_CLK_FREQ;
#else
if(conf->freq_mhz >= 80){
return 80 * MHZ;
}
return (conf->source_freq_mhz * MHZ) / conf->div;
#endif
}

gijs@Imagine:/mnt/c/Users/gijsn/.platformio/packages/framework-arduinoespressif32$ grep APB_CLK_FREQ * -R|grep 1000000
tools/esp32-arduino-libs/esp32/include/soc/esp32/include/soc/soc.h:#define  APB_CLK_FREQ_ROM                            ( 26*1000000 )
tools/esp32-arduino-libs/esp32/include/soc/esp32/include/soc/soc.h:#define  APB_CLK_FREQ                                ( 80*1000000 )       //unit: Hz
tools/esp32-arduino-libs/esp32c2/include/soc/esp32c2/include/soc/soc.h:#define  APB_CLK_FREQ_ROM                            ( 40*1000000 )
tools/esp32-arduino-libs/esp32c2/include/soc/esp32c2/include/soc/soc.h:#define  APB_CLK_FREQ                                ( 40*1000000 )
tools/esp32-arduino-libs/esp32c3/include/soc/esp32c3/include/soc/soc.h:#define  APB_CLK_FREQ_ROM                            ( 40*1000000 )
tools/esp32-arduino-libs/esp32c3/include/soc/esp32c3/include/soc/soc.h:#define  APB_CLK_FREQ                                ( 80*1000000 )
tools/esp32-arduino-libs/esp32c6/include/soc/esp32c6/include/soc/soc.h:#define  APB_CLK_FREQ_ROM                            ( 40*1000000 )
tools/esp32-arduino-libs/esp32c6/include/soc/esp32c6/include/soc/soc.h:#define  APB_CLK_FREQ                                ( 40*1000000 )
tools/esp32-arduino-libs/esp32c6/include/soc/esp32c6/include/soc/soc.h:#define  MODEM_APB_CLK_FREQ                          ( 80*1000000 )
tools/esp32-arduino-libs/esp32h2/include/soc/esp32h2/include/soc/soc.h:#define  APB_CLK_FREQ_ROM                            ( 32*1000000 )
tools/esp32-arduino-libs/esp32h2/include/soc/esp32h2/include/soc/soc.h:#define  APB_CLK_FREQ                                ( 32*1000000 )
tools/esp32-arduino-libs/esp32h2/include/soc/esp32h2/include/soc/soc.h:#define  MODEM_APB_CLK_FREQ                          ( 32*1000000 )
tools/esp32-arduino-libs/esp32s2/include/soc/esp32s2/include/soc/soc.h:#define  APB_CLK_FREQ_ROM                            ( 40*1000000 )
tools/esp32-arduino-libs/esp32s2/include/soc/esp32s2/include/soc/soc.h:#define  APB_CLK_FREQ                                ( 80*1000000 )       //unit: Hz
tools/esp32-arduino-libs/esp32s2/include/soc/esp32s2/include/soc/soc.h:#define  MODEM_REQUIRED_MIN_APB_CLK_FREQ             ( 80*1000000 )
tools/esp32-arduino-libs/esp32s3/include/soc/esp32s3/include/soc/soc.h:#define  APB_CLK_FREQ_ROM                            (40*1000000)
tools/esp32-arduino-libs/esp32s3/include/soc/esp32s3/include/soc/soc.h:#define  APB_CLK_FREQ                                (80*1000000)
tools/esp32-arduino-libs/esp32s3/include/soc/esp32s3/include/soc/soc.h:#define  MODEM_REQUIRED_MIN_APB_CLK_FREQ             (80*1000000)

As can be seen with the H2, this can be something other than 40 or 80 MHz, which makes perfect sense given the H2 runs at 96 MHz CPU clock.
So I wonder whether the defined APB clock frequency for the C2 is correct.
Maybe this one should be 60 MHz?

The APB clock for the C6 seems a bit odd, as it is defined as 40 MHz, while MODEM_APB_CLK_FREQ is 80 MHz. For all other platforms (except the H2) the APB clock has a higher frequency than the ROM (boot?) frequency and the same frequency as MODEM_APB_CLK_FREQ when defined.

N.B. my recent PR on the ESP-IDF repo to fix address calculation for REG_SPI_BASE(i) was incorrectly merged by the IDF team.
So if you run it with the current implementation of the IDF code, you will get an out of bounds error as the base address always returns 0 with this incorrect code.

I'm using this for the REG_SPI_BASE:

cores/esp32/Esp.cpp:  #define REG_SPI_BASE(i)     (DR_REG_SPI1_BASE + (((i)>1) ? (((i)* 0x1000) + 0x20000) : (((~(i)) & 1)* 0x1000 )))
tools/esp32-arduino-libs/esp32/include/soc/esp32/include/soc/spi_reg.h:#define REG_SPI_BASE(i)     (DR_REG_SPI1_BASE + (((i)>1) ? (((i)* 0x1000) + 0x20000) : (((~(i)) & 1)* 0x1000 )))
tools/esp32-arduino-libs/esp32c2/include/soc/esp32c2/include/soc/soc.h:#define REG_SPI_BASE(i)                         (((i)==2) ? (DR_REG_SPI2_BASE) : (DR_REG_SPI0_BASE - ((i) * 0x1000))) // only one GPSPI
tools/esp32-arduino-libs/esp32c3/include/soc/esp32c3/include/soc/soc.h:#define REG_SPI_BASE(i)                         (((i)==2) ? (DR_REG_SPI2_BASE) : (DR_REG_SPI0_BASE - ((i) * 0x1000)))  // only one GPSPI
tools/esp32-arduino-libs/esp32c6/include/soc/esp32c6/include/soc/soc.h:#define REG_SPI_BASE(i)                         (((i)==2) ? (DR_REG_SPI2_BASE) : (DR_REG_SPI0_BASE - ((i) * 0x1000)))  // only one GPSPI on C6
tools/esp32-arduino-libs/esp32h2/include/soc/esp32h2/include/soc/soc.h:#define REG_SPI_BASE(i)                         (DR_REG_SPI2_BASE + (i - 2) * 0x1000)  // only one GPSPI
tools/esp32-arduino-libs/esp32s2/include/soc/esp32s2/include/soc/soc.h:#define REG_SPI_BASE(i)         (DR_REG_SPI2_BASE + (((i)>3) ? (((i-2)* 0x1000) + 0x10000) : ((i - 2)* 0x1000 )))
tools/esp32-arduino-libs/esp32s3/include/soc/esp32s3/include/soc/soc.h:#define REG_SPI_BASE(i)         (((i)==2) ? (DR_REG_SPI2_BASE) : (DR_REG_SPI0_BASE - ((i) * 0x1000)))  // GPSPI2 and GPSPI3

N.B.2 Not 100% sure about this for the ESP32-C6

Sketch

See above

Debug Message

See above

Other Steps to Reproduce

No response

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.
@TD-er TD-er added the Status: Awaiting triage Issue is waiting for triage label Dec 5, 2023
@TD-er
Copy link
Contributor Author

TD-er commented Dec 5, 2023

In the function setCpuFrequencyMhz, the APB frequency is not updated.
See:

#if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(CONFIG_IDF_TARGET_ESP32H2)
if(capb != apb){
//Update REF_TICK (uncomment if REF_TICK is different than 1MHz)
//if(conf.freq_mhz < 80){
// ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = conf.freq_mhz / (REF_CLK_FREQ / MHZ) - 1;
// }
//Update APB Freq REG
rtc_clk_apb_freq_update(apb);
//Update esp_timer divisor
esp_timer_impl_update_apb_freq(apb / MHZ);
}
#endif

However this function is available on those platforms:
https://github.com/espressif/esp-idf/blob/03414a15508036c8fc0f51642aed7a264e9527df/components/soc/esp32c2/include/soc/rtc.h#L405

Thus I would expect calculateApb (or the get function getApbFrequency() ) could use this IDF function:
https://github.com/espressif/esp-idf/blob/03414a15508036c8fc0f51642aed7a264e9527df/components/soc/esp32c2/include/soc/rtc.h#L411C10-L411C30

Edit:
Just tested.
rtc_clk_apb_freq_get() does return 40 MHz on the ESP32-C2

@TD-er
Copy link
Contributor Author

TD-er commented Dec 5, 2023

This makes me wonder, whether this PR I made last month was correct for the ESP32-C2.
The frequencies were taken from esptool.py.

@TD-er
Copy link
Contributor Author

TD-er commented Dec 5, 2023

@me-no-dev
Is it possible to map one of the internal SPI flash pins via the mux to a GPIO which is accessible from the outside?
Then I could measure the actual flash speed being used.

@me-no-dev
Copy link
Member

sounds like you want to copy CLK out. It might be possible, yes. You just need to matrixOut the specific SPI_CLK signal to a GPIO of your choice. Also, I do not guarantee that such signals exist for the matrix, because those SPI buses are "reserved" only for Flash and PSRAM and are always routed directly to the predefined GPIOs. Going through the matrix has some speed penalties.

@TD-er
Copy link
Contributor Author

TD-er commented Dec 6, 2023

I looked through the docs and not yet 100% sure it can't be done.
However, the docs also state there might be configurations with external flash. (the C-series don't seem to support PSRAM)
And on such devices the GPIOs for flash are routed to actual pins.

This raises the question if anyone ever has seen ESP32-C2 units with external flash?

@TD-er
Copy link
Contributor Author

TD-er commented Dec 6, 2023

OK, looks like I need to use gpio_matrix_out:
https://github.com/espressif/esp-idf/blob/30870c819f8bf1be1c8a3b4360be8174a280b40c/components/esp_rom/include/esp32c2/rom/gpio.h#L112-L126C9

And according to its documentation I need to pick one of the functions mentioned in soc/io_mux_reg.h:
https://github.com/espressif/esp-idf/blob/30870c819f8bf1be1c8a3b4360be8174a280b40c/components/soc/esp32c2/include/soc/io_mux_reg.h#L233-L235
But it is unclear to me what the actual function needs to be as those FUNC_SPICLK_SPICLK defines only seem to be low count numbers like 0 or 1.
So should I use PERIPHS_IO_MUX_SPICLK_U instead?
Or is this a bit map?

Or do I need to use SPICLK_OUT_IDX which is defined here ?
Reference to this found here in a post by @Spritetm dating from 2017.

I find the documentation quite confusing as the docs clearly state where to look but those don't seem to refer to a function index, but rather some register (offset?).

This define in gpio_sig_map.h does seem to be more like table 5-2 in ESP8684 TRM (version 1.1).

@TD-er
Copy link
Contributor Author

TD-er commented Dec 6, 2023

Yep, managed to measure the SPI clock freq of the ESP32-C2 using this in my setup:

gpio_matrix_out(2,SPICLK_OUT_IDX, false, false);

image

@me-no-dev
Copy link
Member

This raises the question if anyone ever has seen ESP32-C2 units with external flash?

I don't even have a C2 board yet 😆

@TD-er
Copy link
Contributor Author

TD-er commented Dec 6, 2023

I have bought a few of them from different vendors on Ali just to make sure I get the entire range from Espressif-original to the cheapest garbage available.
Just to be prepared for user reported issues :)

Anyway, it is clear the flash is running at 60 MHz, and thus the reported SPI bus frequency is wrong. (and the IDF documentation for gpio_matrix_out is also wrong and extremely confusing)
But not sure where the reported SPI freq is wrong.
I'll try and look a bit further to see if I can route some clock signals to a GPIO pin to see what frequency those have.

@TD-er
Copy link
Contributor Author

TD-er commented Dec 6, 2023

Just switched to 80 MHz CPU clock and this does change the SPI flash clock to 40 MHz:
image

Just as described in the TRM:
image

I don't know yet when the clock source will be switched from PLL_CLK to XTAL_CLK or RC_FAST_CLK.
But it does affect the APB clock frequency and since a lot of timings are derived from that APB freq, I guess it is incorrect to assume this is a fixed value as now implemented in calculateApb()
And this will probably result in strange issues when clocking down the CPU to run in low-power mode.

I know things like WiFi will not work when running the CPU at clock frequencies less than 80 MHz, but it can be a nice use case to be able to and thus create some kind of low-power system till you need to send your data somewhere.

@Parsaabasi
Copy link

Hello,

Due to the overwhelming volume of issues currently being addressed, we have decided to close the previously received tickets. If you still require assistance or if the issue persists, please don't hesitate to reopen the ticket.

Thanks.

@Parsaabasi Parsaabasi removed the Status: Awaiting triage Issue is waiting for triage label Jan 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants