espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.21k stars 7.34k forks source link

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

Open TD-er opened 8 months ago

TD-er commented 8 months ago

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: https://github.com/espressif/arduino-esp32/blob/609a9477c17cf3329efb6f1e245e5bdb28b61f1a/cores/esp32/esp32-hal-spi.c#L1502-L1525

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: https://github.com/espressif/arduino-esp32/blob/609a9477c17cf3329efb6f1e245e5bdb28b61f1a/cores/esp32/esp32-hal-cpu.c#L157-L166

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

TD-er commented 8 months ago

In the function setCpuFrequencyMhz, the APB frequency is not updated. See: https://github.com/espressif/arduino-esp32/blob/609a9477c17cf3329efb6f1e245e5bdb28b61f1a/cores/esp32/esp32-hal-cpu.c#L233-L244

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 commented 8 months ago

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 commented 8 months ago

@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 commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

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 commented 8 months ago

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.