Про внешний SPI флэш внутри чипов WCH уже везде написали, ситуацию с реальным объемом я описывал https://habr.com/ru/articles/859 054/. Но какая там скорость и как она влияет на производительность системы? WCH на эту тему неоднократно высказывался, правда по китайский и в ответах саппорта на wch.cn:) Общий смысл того, что мне поведал Google переводчик: для кода используйте кэшируемый флэш zero‑wait, все остальное это для пользовательских данных, но если вы уж прям не влезаете в zero-wiat, то можно и в non zero-wait залезть. Крайне содержательно. Чтобы окончательно раскрыть тему, я вооружился тестами производительности CoreMark и в целом получил ответы на 2 своих главных вопроса: какая частота доступа к физическому SPI флэшу и как стратегия его применения в реальных проектах. Сейчас расскажу.

Итак, исходные данные. Прошивка запускалась в zero-wait. Код бенчмарка, исполняемый между функциями замера времени - мапился в верхние адреса за пределы кэшируемого флэша. Компилятор GCC8.2.0 -03, все остальное по дефолту из IDE. Время считалось на SysTick. Тесты проводились для стандартных частоты 48,56,72,96,120 и 144Mhz. На каждой частоте бенчмарк прогонялся 3 раза: в zero-wait, non zero-wait и non zero-wait с включенным режимом ускорения чтения из флэша (Flash Enhanced Read Mode). Кол-во итераций во всех тестах 4000.

Для CH32V203C8T6 rev0 получилось:

zero-wait флэш

non-zero wait флэш

non zero-wait флэш (FERM)

Частота

Цикл CoreMark в сек

Цикл CoreMark на Mhz

Цикл CoreMark в сек

Цикл CoreMark на Mhz

Цикл CoreMark в сек

Цикл CoreMark на Mhz

144Mhz

375,768584

2,60950405

34,664655

0,24072677

37,351394

0,25938468

120Mhz

313,140486

2,60950405

29,190354

0,24325295

31,126162

0,25938468

96Mhz

250,512389

2,60950405

23,172174

0,24137681

24,90093

0,25938468

72Mhz

187,88429

2,60950402

17,332327

0,24072676

18,675697

0,25938468

56Mhz

146,132227

2,60950405

13,4806991

0,24072677

14,525542

0,25938467

48Mhz

125,256195

2,60950406

11,554885

0,24072677

12,4504645

0,25938467

Репорт для 144Mhz zero-wait

SystemClk:144 000 000 
ChipID:20 310 500 
2K performance run parameters for coremark.
CoreMark Size: 666 
Total ticks: 191 607 290 
Total time (secs): 10.644 849 
Iterations/Sec: 375.768 584 
Iterations: 4000 
Compiler version: GCC8.2.0 
Compiler flags: o3 
Memory location: STACK
seedcrc: 0xe9f5 
[0]crclist: 0xe714 
[0]crcmatrix: 0×1fd7 
[0]crcstate: 0×8e3a
[0]crcfinal: 0×65c5 
Correct operation validated. See README.md for run and reporting rules.
CoreMark 1.0: 375.768 584 / GCC8.2.0 o3 / STACK

Репорт для 144Mhz non zero-wait Flash Enhanced Read Mode

SystemClk:144000000
ChipID:20310500
2K performance run parameters for coremark.
CoreMark Size : 666
Total ticks : 1927638880
Total time (secs): 107.091049
Iterations/Sec : 37.351394
Iterations : 4000
Compiler version : GCC8.2.0
Compiler flags : o3
Memory location : STACK
seedcrc : 0xe9f5
[0]crclist : 0xe714
[0]crcmatrix : 0x1fd7
[0]crcstate : 0x8e3a
[0]crcfinal : 0x65c5
Correct operation validated. See
README.md for run and reporting rules.
CoreMark 1.0 : 37.351394 / GCC8.2.0 o3 / STACK

Остальные репорты публиковать смысла не вижу.

Теперь V303 и V307. Результаты идентичны.

zero wait флэш

non zero wait флэш

non zero wait флэш (FERM)

Частота

Цикл в сек

Цикл на Mhz

Цикл в сек

Цикл на Mhz

Цикл в сек

Цикл на Mhz

144

366,415588

2,54455269

60,813471

0,42231577

68,385467

0,47489907

120

305,346324

2,5445527

50,677894

0,42231578

56,987889

0,47489907

96

244,277059

2,54455269

40,542315

0,42231578

45,590311

0,47489907

72

183,207794

2,54455269

30,406736

0,42231577

34,192733

0,47489906

56

142,494953

2,54455273

23,649683

0,42231576

26,594348

0,47489907

48

122,13853

2,54455270

20,271157

0,42231577

22,795156

0,47489908

Репорт для СH32V303/V307 144Mhz zero-wait

SystemClk:144000000
ChipID:30330514
2K performance run parameters for coremark.
CoreMark Size : 666
Total ticks : 196498191
Total time (secs): 10.916566
Iterations/Sec : 366.415587
Iterations : 4000
Compiler version : GCC8.2.0
Compiler flags : o3
Memory location : STACK
seedcrc : 0xe9f5
[0]crclist : 0xe714
[0]crcmatrix : 0x1fd7
[0]crcstate : 0x8e3a
[0]crcfinal : 0x65c5
Correct operation validated. See
README.md for run and reporting rules.
CoreMark 1.0 : 366.415587 / GCC8.2.0 o3 / STACK

Репорт для СH32V303/V307 non zero-wait Flash Enhanced Read Mode

SystemClk:144000000
ChipID:30330514
2K performance run parameters for coremark.
CoreMark Size : 666
Total ticks : 1052855275
Total time (secs): 58.491960
Iterations/Sec : 68.385467
Iterations : 4000
Compiler version : GCC8.2.0
Compiler flags : o3
Memory location : STACK
seedcrc : 0xe9f5
[0]crclist : 0xe714
[0]crcmatrix : 0x1fd7
[0]crcstate : 0x8e3a
[0]crcfinal : 0x65c5
Correct operation validated. See
README.md for run and reporting rules.
CoreMark 1.0 : 68.385467 / GCC8.2.0 o3 / STACK

Валидация результата.

Для начала соотнесем результаты с тем, что известно на эту тему по CH32V307 в zero-wait 144Mhz.

https://gitee.com/elecb/ch32v307_core-mark - CoreMark 1.0: 380.662352 / GCC8.2.0 -o3 / СТЕК

https://zhuanlan.zhihu.com/p/496255489 - CoreMark 1.0: 379.982521 / GCC8.2.0 -o3 / STACK

https://bbs.21ic.com/icview-3164492-1-1.html CoreMark 1.0: 382.464221 / GCC8.2.0 -ofast -o3 / STACK

https://www.eembc.org/viewer/?benchmark_seq=13505. CoreMark 1.0: 459.16 / GCC12.1.0 --O3 -funroll-all-loops -finline-limit=600 -ftree-dominator-opts -fno-if-conversion2 -fselective-scheduling -fno-code-hoisting

Последние мое любимое, но на таких же хардкорных ключах я получал аналогичный результат. Порядок сходиться, можно считать для что zero-wait я получил валидный результат. Разница объясняется количеством итераций и разрядностью счетчика времени. Я считаю время по SysTick с делителем 8 (т.е. 18Mhz). Во всех приведенных выше примерах - таймер на 1кHZ или около того. Т.е. инструмент измерения (таймер) на 5 и более порядков меньшей разрядности, чем измеряемый процесс. Что могло у них пойти не так ?:) Но для быстрого флэша это не так критично. Теперь интереснее.

Те, кто интересуется контроллерами WCH, могли встречать различные оценки скорости флэша в верхних адресах. Для V203 я таких данных не находил, а вот V307 тестировали. Я встречал оценки в 10 и даже 18 раз медленнее. Удалось найти репорт по этому поводу https://blog.csdn.net/mx1117/article/details/126094948

Из репорта видно, что компилируется бенчмарк на -Os. Тут в целом все очевидно, можно в ASM залезть. Поcкольку компилятор разворачивает циклы на O3, поток команд теста по большей части состоит из команд работы с регистрами. А вот на Os большая часть команд с операндами в тот же самом медленном флэше. При тестах на O3 замедление происходит на выборке команды и результат характеризует в большей степени производительность флэш. В случае с Os замедление происходит до кучи еще и в на выборке операнда. Что характеризуют результаты тестов на Os аллах его знает. Формально производительность системы, но медленного флэша в WCH в 3 раза больше чем кэшируемого, какой практический смысл исполняться там с оптимизацией по размеру?

Ну и использование для расчета времени таймера, а не SysTick. А еще видимо что бы не ждать, или/и что бы не переполнять таймер, сокращают кол-во итераций теста при исполнении в верхних адресах. Два этих параметра приводят к тому, что в верхних адресах помимо проблем c Os, еще и накладываются проблемы с расчетом. Мне лень повторять тесты при таких же условиях. Но на Os я тем не менее эксперимент провел, результат предсказуемо в 2 раза хуже для верхних адресов, чем для O3.

Анализ результатов.

С валидностью надеюсь разобрались. Теперь можно попробовать накинуть аналитики. Первое и самое неожиданное, V3 медленнее в тестах чем V2. Чуть чуть, но тем не менее. Для верности я сравнил оба проекта на тему настроек IDE, размера кода исполняемого бенчамарака и бегло глянул .lst. Все идентично рубль в рубль. Тогда какого? Может не прав, но тут 2 разных ядра QingKeV4B и QingKeV4F. Помимо FPU, который не используется в тестах, на 4F есть модуль защиты памяти. Т.е. по идее это дополнительный блок в цепочке ядро-память. Не однозначно, но MPU единственное видимое отличие, и вроде как логично объясняет небольшую разницу в скорости.

Но это мелочь, главное что же там за флэш то? Я думаю ответ тут. https://www.eembc.org/viewer/?benchmark_seq=13659 Конечно результаты бенчмарка очень сильно зависят от кучи параметров, ну тут контроллер на аналогичном ядре QingKeV4C. Это тот же QingKeV4B, только с модулем MPU. Исходя из сказанного выше, оно может быть чуть медленнее. И таки да, результаты его теста на 48Mhz чуть медленнее чем V2 на 144Mhz в non zero-wait. Чем еще интересен CH32X033F8P6? В нем однозначно определена частота доступа к флэш -это 20Mhz. И есть регистр Latensy, в котором можно докинуть wait-stait при работе ядра выше 20Mhz. Я думаю, с высокой долей вероятности, в случае с CH32V203 мы имеет дело с 20Mhz внешнем SPI флэшем. И загадочным контроллером Flash, который копирует при старте часть флэша в SRAM, а в процессе работы, в случае обращения со стороны ядра за пределы кэша, автоматом накидывает условно 10 wait-stait. Поэтому мы получаем равномерное падение производительности в верхних адресах на всех частотах. Еще одно отличие от CH32X033F8P6 , это тактирование записи во флэш. Во всех младших семействах WCH тактирование идет от HSI напрямую, в случае CH32X033F8P6 от всторенного 8Mhz. А вот на V2 и V3 тактирование с системной шины APH, через делитель, но с ограничением в 60Mhz. Если принять, что в V203 частота обращения к флэш 20Mhz, то на кой черт контроллеру флэш 60, при том что аналогичное ядро справляется с тем же самым на 8? А видимо для синхронизации кэша при записи. Допустим спорное утверждение, но логика в этом есть.

А что с V3? Ну там падение производительности в 5 раз. Это в 2 раза меньше чем на V2. Если поискать в закромах WCH, то находиться CH32L103 на ядре QingKeV4C. Там есть регистр latency, но частота обращения к флэш 40Mhz.Так что очень похоже, что в случае с V303/ V307 мы имеем дело как раз с флэшем на 40Mhz.

Выводы.

Помимо размещения данных в верхних адресах, волне можно придумать части кода для переноса в non zero-wait зону флэш. Разницы по сути нет, при размещение данных замедление идет на выборке операнда, при размещение кода - точно такое же при выборке команды. Поэтому для кода оптимизация по скорости ( желательно максимально хардкорная, см. ссылку выше, CoreMark 459 против 380 на просто O3). Ну и Flash Enhanced Read Mode для верхних адресов желателен, поскольку добавляет около 8% ускорения. В случае с V3 имеет падение скорости доступа в 5 раз, в случае с V2 - в 10 раз. Не стоит путать эти цифры с падением производительности. Она может быть выше если код исполняемый в верхних адресах использует данные из той же зоны флэш. Как в случае с оптимизацией по размеру. Тогда падение производительности будет выше чем разница в скорости доступа.

Комментарии (2)


  1. Kudriavyi
    13.05.2025 13:56

    Статья неплохая, и тема очень интересная, за содержание однозначно плюс! Но читать местами тяжело, сумбурно.


    1. Smoke666 Автор
      13.05.2025 13:56

      Спасибо, может перепишу когда уляжется все в голове. Сабж мой личный ад последней пары дней, написал с терапевтической целю:)