Про внешний 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 раз. Не стоит путать эти цифры с падением производительности. Она может быть выше если код исполняемый в верхних адресах использует данные из той же зоны флэш. Как в случае с оптимизацией по размеру. Тогда падение производительности будет выше чем разница в скорости доступа.
Kudriavyi
Статья неплохая, и тема очень интересная, за содержание однозначно плюс! Но читать местами тяжело, сумбурно.
Smoke666 Автор
Спасибо, может перепишу когда уляжется все в голове. Сабж мой личный ад последней пары дней, написал с терапевтической целю:)