1
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
70
75
76
77
78
79
80
81
84
85
86
89
90
91
96
97
98
104
105
106
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
135
136
137
140
141
142
147
148
149
155
156
157
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
232
233
238
241
242
243
244
245
248
249
250
256
257
258
259
260
261
262
263
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
297
298
299
300
301
302
303
304
305
306
312
313
314
315
316
317
321
322
326
327
328
329
330
331
332
333
334
337
338
339
340
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
380
381
382
385
386
387
388
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
464
465
466
469
470
471
472
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
499
500
501
502
509
510
511
512
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
534
535
536
537
538
539
540
545
546
553
554
555
556
557
564
565
566
567
568
569
570
571
572
573
574
577
578
579
580
588
589
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
615
616
619
/* ... */
#include <stdlib.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_types.h"
#include "esp_log.h"
#include "soc/io_mux_reg.h"
#include "soc/soc.h"
#include "hal/spi_flash_hal.h"
#include "hal/cache_hal.h"
#include "hal/cache_ll.h"
#include "esp_private/mspi_timing_tuning.h"
#include "esp_private/mspi_timing_config.h"
#include "mspi_timing_by_mspi_delay.h"
#include "mspi_timing_by_dqs.h"16 includes
#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY || SOC_MEMSPI_TIMING_TUNING_BY_DQS
#include "mspi_timing_tuning_configs.h"
#include "hal/mspi_timing_tuning_ll.h"/* ... */
#endif
#if SOC_MEMSPI_CLK_SRC_IS_INDEPENDENT
#include "hal/spimem_flash_ll.h"
#endif
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
#include "hal/mspi_timing_tuning_ll.h"
#endif
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#include "esp_ipc_isr.h"
#endif
#if CONFIG_ESPTOOLPY_FLASHFREQ_120M
#define FLASH_FREQUENCY_MHZ 120
#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define FLASH_FREQUENCY_MHZ 80
#elif CONFIG_ESPTOOLPY_FLASHFREQ_64M
#define FLASH_FREQUENCY_MHZ 64
#elif CONFIG_ESPTOOLPY_FLASHFREQ_60M
#define FLASH_FREQUENCY_MHZ 60
#elif CONFIG_ESPTOOLPY_FLASHFREQ_48M
#define FLASH_FREQUENCY_MHZ 48
#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M
#define FLASH_FREQUENCY_MHZ 40
#elif CONFIG_ESPTOOLPY_FLASHFREQ_32M
#define FLASH_FREQUENCY_MHZ 32
#elif CONFIG_ESPTOOLPY_FLASHFREQ_30M
#define FLASH_FREQUENCY_MHZ 30
#elif CONFIG_ESPTOOLPY_FLASHFREQ_26M
#define FLASH_FREQUENCY_MHZ 26
#elif CONFIG_ESPTOOLPY_FLASHFREQ_24M
#define FLASH_FREQUENCY_MHZ 24
#elif CONFIG_ESPTOOLPY_FLASHFREQ_20M
#define FLASH_FREQUENCY_MHZ 20
#elif CONFIG_ESPTOOLPY_FLASHFREQ_16M
#define FLASH_FREQUENCY_MHZ 16
#elif CONFIG_ESPTOOLPY_FLASHFREQ_15M
#define FLASH_FREQUENCY_MHZ 15
#endif
/* ... */
typedef enum {
MSPI_TIMING_TUNING_MSPI_DIN_DUMMY,
MSPI_TIMING_TUNING_MSPI_DQS_PHASE,
MSPI_TIMING_TUNING_MSPI_DELAYLINE,
}{ ... } mspi_timing_tuning_t;
typedef struct mspi_tuning_cfg_drv_s mspi_tuning_cfg_drv_t;
__attribute__((unused)) const static char *TAG = "MSPI Timing";
struct mspi_tuning_cfg_drv_s {
/* ... */
mspi_timing_tuning_t flash_tuning_type;
/* ... */
uint32_t sweep_test_nums;
/* ... */
void (*flash_init_mspi)(uint32_t flash_freq_mhz);
/* ... */
void (*flash_tune_mspi)(const void *configs, uint8_t id);
/* ... */
void (*flash_read)(uint8_t *buf, uint32_t addr, uint32_t len);
/* ... */
uint32_t (*flash_select_best_tuning_config)(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr);
/* ... */
void (*flash_set_best_tuning_config)(const void *configs, uint8_t best_id);
/* ... */
mspi_timing_tuning_t psram_tuning_type;
/* ... */
void (*psram_init_mspi)(uint32_t psram_freq_mhz);
/* ... */
void (*psram_tune_mspi)(const void *configs, uint8_t id);
/* ... */
void (*psram_read)(uint8_t *buf, uint32_t addr, uint32_t len);
/* ... */
uint32_t (*psram_select_best_tuning_config)(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr);
/* ... */
void (*psram_set_best_tuning_config)(const void *configs, uint8_t best_id);
}{ ... };
static mspi_tuning_cfg_drv_t s_tuning_cfg_drv = {};
void s_register_config_driver(mspi_tuning_cfg_drv_t *cfg_drv, bool is_flash)
{
if (is_flash) {
s_tuning_cfg_drv.flash_tuning_type = cfg_drv->flash_tuning_type;
s_tuning_cfg_drv.flash_init_mspi = cfg_drv->flash_init_mspi;
s_tuning_cfg_drv.flash_tune_mspi = cfg_drv->flash_tune_mspi;
s_tuning_cfg_drv.flash_read = cfg_drv->flash_read;
s_tuning_cfg_drv.flash_select_best_tuning_config = cfg_drv->flash_select_best_tuning_config;
s_tuning_cfg_drv.flash_set_best_tuning_config = cfg_drv->flash_set_best_tuning_config;
}{...} else {
s_tuning_cfg_drv.psram_tuning_type = cfg_drv->psram_tuning_type;
s_tuning_cfg_drv.psram_init_mspi = cfg_drv->psram_init_mspi;
s_tuning_cfg_drv.psram_tune_mspi = cfg_drv->psram_tune_mspi;
s_tuning_cfg_drv.psram_read = cfg_drv->psram_read;
s_tuning_cfg_drv.psram_select_best_tuning_config = cfg_drv->psram_select_best_tuning_config;
s_tuning_cfg_drv.psram_set_best_tuning_config = cfg_drv->psram_set_best_tuning_config;
}{...}
s_tuning_cfg_drv.sweep_test_nums = cfg_drv->sweep_test_nums;
}{ ... }
#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
/* ... */
static void s_sweep_for_success_sample_points(uint8_t *reference_data, void *config, bool is_flash, uint32_t *out_array)
{
const mspi_timing_config_t *timing_config = (const mspi_timing_config_t *)config;
uint32_t config_idx = 0;
uint8_t read_data[MSPI_TIMING_TEST_DATA_LEN] = {0};
for (config_idx = 0; config_idx < timing_config->available_config_num; config_idx++) {
out_array[config_idx] = 0;
for (int i = 0; i < s_tuning_cfg_drv.sweep_test_nums; i++) {
memset(read_data, 0, MSPI_TIMING_TEST_DATA_LEN);
#if MSPI_TIMING_FLASH_NEEDS_TUNING
if (is_flash) {
s_tuning_cfg_drv.flash_tune_mspi(timing_config, config_idx);
s_tuning_cfg_drv.flash_read(read_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, sizeof(read_data));
}{...}
/* ... */#endif
#if MSPI_TIMING_PSRAM_NEEDS_TUNING
if (!is_flash) {
s_tuning_cfg_drv.psram_tune_mspi(timing_config, config_idx);
s_tuning_cfg_drv.psram_read(read_data, MSPI_TIMING_PSRAM_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN);
}{...}
/* ... */#endif
if (memcmp(reference_data, read_data, sizeof(read_data)) == 0) {
out_array[config_idx] += 1;
}{...}
}{...}
}{...}
ESP_EARLY_LOGD(TAG, "test nums: %" PRIu32 ", test result: [id][good/bad][good_times]:", s_tuning_cfg_drv.sweep_test_nums);
for (config_idx = 0; config_idx < timing_config->available_config_num; config_idx++) {
ESP_EARLY_LOGD(TAG, "[%"PRIu32"][%s][%" PRIu32 "] ", config_idx, out_array[config_idx] == s_tuning_cfg_drv.sweep_test_nums ? "good" : "bad", out_array[config_idx]);
}{...}
}{...}
/* ... */
static void s_find_max_consecutive_success_points(uint32_t *array, uint32_t size, uint32_t *out_length, uint32_t *out_end_index)
{
uint32_t max = 0;
uint32_t match_num = 0;
uint32_t i = 0;
uint32_t end = 0;
while (i < size) {
if (array[i] == s_tuning_cfg_drv.sweep_test_nums) {
match_num++;
}{...} else {
if (match_num > max) {
max = match_num;
end = i - 1;
}{...}
match_num = 0;
}{...}
i++;
}{...}
*out_length = match_num > max ? match_num : max;
*out_end_index = match_num == size ? size : end;
}{...}
static void s_select_best_tuning_config(mspi_timing_config_t *config, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_flash)
{
const mspi_timing_config_t *timing_config = (const mspi_timing_config_t *)config;
uint32_t best_point = 0;
if (is_flash) {
#if MSPI_TIMING_FLASH_DTR_MODE
best_point = s_tuning_cfg_drv.flash_select_best_tuning_config(timing_config, consecutive_length, end, reference_data, IS_DDR);
#elif MSPI_TIMING_FLASH_STR_MODE
best_point = s_tuning_cfg_drv.flash_select_best_tuning_config(timing_config, consecutive_length, end, NULL, IS_SDR);
#endif
s_tuning_cfg_drv.flash_set_best_tuning_config(timing_config, best_point);
}{...} else {
#if MSPI_TIMING_PSRAM_DTR_MODE
best_point = s_tuning_cfg_drv.psram_select_best_tuning_config(timing_config, consecutive_length, end, reference_data, IS_DDR);
#if CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR
mspi_timing_setting_temperature_adjustment_best_point(best_point);
#endif/* ... */
#elif MSPI_TIMING_PSRAM_STR_MODE
best_point = s_tuning_cfg_drv.psram_select_best_tuning_config(timing_config, consecutive_length, end, NULL, IS_SDR);
#endif
s_tuning_cfg_drv.psram_set_best_tuning_config(timing_config, best_point);
}{...}
}{...}
static void s_do_tuning(uint8_t *reference_data, mspi_timing_config_t *config, bool is_flash)
{
/* ... */
uint32_t consecutive_length = 0;
uint32_t last_success_point = 0;
uint32_t sample_result[MSPI_TIMING_CONFIG_NUM_MAX] = {0};
#if MSPI_TIMING_FLASH_NEEDS_TUNING
if (is_flash) {
s_tuning_cfg_drv.flash_init_mspi(FLASH_FREQUENCY_MHZ);
}{...}
/* ... */#endif
#if MSPI_TIMING_PSRAM_NEEDS_TUNING
if (!is_flash) {
s_tuning_cfg_drv.psram_init_mspi(CONFIG_SPIRAM_SPEED);
}{...}
/* ... */#endif
s_sweep_for_success_sample_points(reference_data, config, is_flash, sample_result);
s_find_max_consecutive_success_points(sample_result, config->available_config_num, &consecutive_length, &last_success_point);
s_select_best_tuning_config(config, consecutive_length, last_success_point, reference_data, is_flash);
}{...}
/* ... */#endif
/* ... */
#if MSPI_TIMING_FLASH_NEEDS_TUNING
void mspi_timing_flash_tuning(void)
{
/* ... */
mspi_timing_enter_low_speed_mode(true);
#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
mspi_tuning_cfg_drv_t drv = {
.flash_tuning_type = MSPI_TIMING_TUNING_MSPI_DIN_DUMMY,
.sweep_test_nums = 1,
.flash_init_mspi = mspi_timing_flash_init,
.flash_tune_mspi = mspi_timing_config_flash_set_tuning_regs,
.flash_read = mspi_timing_config_flash_read_data,
.flash_select_best_tuning_config = mspi_timing_flash_select_best_tuning_config,
.flash_set_best_tuning_config = mspi_timing_flash_set_best_tuning_config,
}{...};
bool is_flash = true;
s_register_config_driver(&drv, is_flash);
mspi_timing_ll_enable_flash_variable_dummy(1, false);
uint8_t reference_data[MSPI_TIMING_TEST_DATA_LEN] = {0};
s_tuning_cfg_drv.flash_read(reference_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, sizeof(reference_data));
mspi_timing_config_t timing_configs = {0};
mspi_timing_get_flash_tuning_configs(&timing_configs);
/* ... */
#endif
s_do_tuning(reference_data, &timing_configs, true);
mspi_timing_enter_high_speed_mode(true);
}{...}
/* ... */#else
void mspi_timing_flash_tuning(void)
{
}{ ... }
/* ... */#endif
/* ... */
#if MSPI_TIMING_PSRAM_NEEDS_TUNING
void mspi_timing_psram_tuning(void)
{
/* ... */
mspi_timing_enter_low_speed_mode(true);
uint8_t reference_data[MSPI_TIMING_TEST_DATA_LEN];
mspi_timing_config_psram_prepare_reference_data(reference_data, MSPI_TIMING_TEST_DATA_LEN);
mspi_timing_config_psram_write_data(reference_data, MSPI_TIMING_PSRAM_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN);
#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
mspi_tuning_cfg_drv_t drv = {
.psram_tuning_type = MSPI_TIMING_TUNING_MSPI_DIN_DUMMY,
.sweep_test_nums = 1,
.psram_init_mspi = mspi_timing_psram_init,
.psram_tune_mspi = mspi_timing_config_psram_set_tuning_regs,
.psram_read = mspi_timing_config_psram_read_data,
.psram_select_best_tuning_config = mspi_timing_psram_select_best_tuning_config,
.psram_set_best_tuning_config = mspi_timing_psram_set_best_tuning_config,
}{...};
bool is_flash = false;
s_register_config_driver(&drv, is_flash);
mspi_timing_config_t timing_configs = {};
mspi_timing_get_psram_tuning_configs(&timing_configs);
mspi_timing_ll_enable_flash_variable_dummy(1, false);
s_do_tuning(reference_data, &timing_configs, false);/* ... */
#endif
#if SOC_MEMSPI_TIMING_TUNING_BY_DQS
bool is_flash = false;
mspi_timing_config_t timing_configs = {};
mspi_tuning_cfg_drv_t drv = {
.psram_tuning_type = MSPI_TIMING_TUNING_MSPI_DQS_PHASE,
.sweep_test_nums = 1,
.psram_init_mspi = mspi_timing_psram_init,
.psram_tune_mspi = mspi_timing_config_psram_set_tuning_phase,
.psram_read = mspi_timing_config_psram_read_data,
.psram_select_best_tuning_config = mspi_timing_psram_select_best_tuning_phase,
.psram_set_best_tuning_config = mspi_timing_psram_set_best_tuning_phase,
}{...};
s_register_config_driver(&drv, is_flash);
mspi_timing_get_psram_tuning_phases(&timing_configs);
s_do_tuning(reference_data, &timing_configs, false);
drv = (mspi_tuning_cfg_drv_t) {
.psram_tuning_type = MSPI_TIMING_TUNING_MSPI_DELAYLINE,
.sweep_test_nums = MSPI_TIMING_DELAYLINE_TEST_NUMS,
.psram_init_mspi = mspi_timing_psram_init,
.psram_tune_mspi = mspi_timing_config_psram_set_tuning_delayline,
.psram_read = mspi_timing_config_psram_read_data,
.psram_select_best_tuning_config = mspi_timing_psram_select_best_tuning_delayline,
.psram_set_best_tuning_config = mspi_timing_psram_set_best_tuning_delayline,
}{...};
s_register_config_driver(&drv, is_flash);
mspi_timing_get_psram_tuning_delaylines(&timing_configs);
s_do_tuning(reference_data, &timing_configs, false);/* ... */
#endif
mspi_timing_enter_high_speed_mode(true);
}{...}
/* ... */
#else
void mspi_timing_psram_tuning(void)
{
}{ ... }
/* ... */#endif
/* ... */
void mspi_timing_enter_low_speed_mode(bool control_spi1)
{
#if SOC_MEMSPI_FLASH_CLK_SRC_IS_INDEPENDENT
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
mspi_ll_clock_src_sel(MSPI_CLK_SRC_XTAL);
#else
spimem_flash_ll_set_clock_source(MSPI_CLK_SRC_ROM_DEFAULT);
#endif/* ... */
#endif
#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
/* ... */
mspi_timing_config_set_flash_clock(20, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1);
mspi_timing_config_set_psram_clock(20, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1);/* ... */
#endif
#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
mspi_timing_flash_config_clear_tuning_regs(control_spi1);
mspi_timing_psram_config_clear_tuning_regs(control_spi1);/* ... */
#endif
}{ ... }
/* ... */
void mspi_timing_enter_high_speed_mode(bool control_spi1)
{
#if SOC_MEMSPI_FLASH_CLK_SRC_IS_INDEPENDENT
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
mspi_ll_clock_src_sel(MSPI_CLK_SRC_SPLL);
#else
spimem_flash_ll_set_clock_source(MSPI_CLK_SRC_DEFAULT);
#endif/* ... */
#endif
#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
/* ... */
mspi_timing_config_set_flash_clock(FLASH_FREQUENCY_MHZ, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, control_spi1);
#if CONFIG_SPIRAM
mspi_timing_config_set_psram_clock(CONFIG_SPIRAM_SPEED, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, control_spi1);
#endif /* ... */
#endif
#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
mspi_timing_flash_config_set_tuning_regs(control_spi1);
mspi_timing_psram_config_set_tuning_regs(control_spi1);/* ... */
#endif
}{ ... }
void mspi_timing_change_speed_mode_cache_safe(bool switch_down)
{
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE && !CONFIG_FREERTOS_UNICORE
esp_ipc_isr_stall_other_cpu();/* ... */
#endif
/* ... */
#if SOC_CACHE_FREEZE_SUPPORTED
cache_hal_freeze(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
#endif
if (switch_down) {
mspi_timing_enter_low_speed_mode(false);
}{...} else {
mspi_timing_enter_high_speed_mode(false);
}{...}
#if SOC_CACHE_FREEZE_SUPPORTED
cache_hal_unfreeze(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL);
#endif
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE && !CONFIG_FREERTOS_UNICORE
esp_ipc_isr_release_other_cpu();
#endif
}{ ... }
/* ... */
bool spi_flash_timing_is_tuned(void)
{
#if MSPI_TIMING_MSPI1_IS_INVOLVED
#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
return true;/* ... */
#else
return false;/* ... */
#endif/* ... */
#else
return false;/* ... */
#endif
}{ ... }
#if MSPI_TIMING_MSPI1_IS_INVOLVED && (MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING)
void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing_config)
{
out_timing_config->clock_config.spimem = mspi_timing_config_get_flash_clock_reg();
out_timing_config->extra_dummy = mspi_timing_config_get_flash_extra_dummy();
mspi_timing_config_get_cs_timing(&out_timing_config->cs_setup, &out_timing_config->cs_hold);
}{...}
/* ... */#else
void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing_config)
{
abort();
}{ ... }
/* ... */#endif
/* ... */
void mspi_timing_set_pin_drive_strength(void)
{
#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
mspi_timing_ll_set_all_pin_drive(0, 3);/* ... */
#endif
}{ ... }