[09.01.2024] по ходу подпрыгивании на тему "подъема-взлета"
ch32v003 всунул в него CoreMark. промерил... на данный момент
1. допортил gcc на поддержку ядра qkv2a ( оптимизация под набор инструкций и добавка генерации обычных прерываний и "лысых" когда включена аппаратное двухуровневое сохранение)
2. допили свой sdk до состояния: интерфейс к ядру + fpic + systick + rcc. осталось остальную переферию обернуть. ее немного в этих микросхемках. может за выходные половинку сделаю.
3. ядро ricsv32ec - тоесть урезанное до 16 штук регистры и к сожалению отсутствует умножитель. если бы он был.... думаю результат в полтора раза больше был бы? побыстрее stm32l0xx c cortex-m0plus
4 на прогоне CoreMark потребление - 6.0 мА при 5В питания.
5. всунул CoreMark для тестирования сравнения. вывод результата через uart на консоль хост машины. получилось 94,914 баллов. которые равны числу итераций в секунду.
этот результат позволяет заключить что микросхемка по мнению СoreMark эквивалентна stm32f0xx .
размер прошивочки
memutz .././../out/image.elf 16K 2K
section info:
sec name size increase[%]
.text 11180 0 (0.000000%)
.data 24 0 (0.000000%)
.bss 1024 0 (0.000000%)
utilization:
ram : 51.1719% 0 (0.000000%)
flash: 68.3838% 0 (0.000000%)
вот если кому интересно что генерится из чего - интермикс исходного кода и асма этой прошивки. кстате - обратите внимание, objdump научился зачем то показывать трассы переходов :) прикольненько
image.lss
первые выводы - оно работает. явных косяков покане обнаружил.
аппаратное сохранение контекста - гожая метода, почему нельзя сделать глубину хотя бы 8. и делать везде и всегда? 8*16(32) *4 байтов скрытого СОЗУ пожалели?
вход в прерывание аж тремя способами - в таблице адреса перехода, инструкция перехода и VFT-метод
"релаксация" на линковке дает -5..15% уменьшения кода и столько же его же ускорения. кто не в курсе - механизм релакса - способ сжать инструкции по адресным полям используя регистр gp процессоров RISCV. обращения к адресам в диапазоне +-2К от значения хранящегося в регистре gp требует на одну иструкцию вычисления адреса меньше. это 30 процентов от всего кода обращения.
вот тот же код но с выключенной релаксной оптимизацией на ликовке
image.lss
memutz .././../out/image.elf 16K 2K
section info:
sec name size increase[%]
.text 12160 980 (8.765650%)
.data 24 0 (0.000000%)
.bss 1024 0 (0.000000%)
utilization:
ram : 51.1719% 0 (0.000000%)
flash: 74.3652% 980 (8.746874%)
на круг при размере кода 11Kb использование этого трюка сократило код на 980 байт, это почти 10%.
вот красноречивый пример как это работает на примере обработчика прерывания sys_tick - в нем сбрасывается флаг переполнения и инкрементируется счеткик миллисикекуд для измерения времени теста
с релаксом
00000230 <ch32v00x::sys_tick_irq_handler()>:
void __ai__ val( const T v) { NRO *(((volatile T*)this)) = v; }
230: e000f7b7 lui a5,0xe000f
234: 0007a223 sw zero,4(a5) # e000f004 <__stack_end__+0xc000e804>
namespace ch32v00x
{
irq_handler_hpei(sys_tick)
{
sys_tick.compare_flag_clear();
tick_count = tick_count + 1 ;
238: e181a283 lw t0,-488(gp) # 20000018 <tick_count>
23c: 00128313 add t1,t0,1
240: e061ac23 sw t1,-488(gp) # 20000018 <tick_count>
}
244: 30200073 mret
без релакса
0000025e <ch32v00x::sys_tick_irq_handler()>:
void __ai__ val( const T v) { NRO *(((volatile T*)this)) = v; }
25e: e000f7b7 lui a5,0xe000f
262: 0007a223 sw zero,4(a5) # e000f004 <__stack_end__+0xc000e804>
namespace ch32v00x
{
irq_handler_hpei(sys_tick)
{
sys_tick.compare_flag_clear();
tick_count = tick_count + 1 ;
266: 20000737 lui a4,0x20000
26a: 01872283 lw t0,24(a4) # 20000018 <tick_count>
26e: 00128313 add t1,t0,1
272: 00672c23 sw t1,24(a4)
}
276: 30200073 mret
я потратил время разобраться как это работает, научился компиллеру объяснять и он с радостью оптимизирует код.
если понадобится - разжую. сам не сразу понял как это заставить работать.
беглым признаком что ваш компиллер и линкер умеют это делать - наличие в коде на каждом углу вычисление адресов через регистр gp, например так:
lw t0,-488(gp) # 20000018 <tick_count>
в приведенных выше image.lss это очень хорошо видно. во втором гораздо больше калорий тратится на вычисление адресов в лоб, в первом через относительный адрес используя gp.
пока что из недостатоков только отсутствие целочисленного перемножителя и делителя в процессоре
ну как то так.. за 128 рулей просто замечательно.
китайцы сдирают микросхемы с буржуев. а я сильно думаю содрать с китайцев
[обновление 2.07.23]
доковырял rrc - есть новые сведения.. минус две микросхемы
1. есть засада о которой нужно знать. при использовании кварцевого генератора hse нужно перед его запуском замапить пины gpioa_pin1 gpioa_pin2 на генератор. иначе они как и положено просто gpioa. второе - выставить латентность wait1 (это не точно), и только потом запускать hse.
я писал код с нуля и это поздно понял. две микросхемы ушли в астрал, такое ощущение что если не проверить что генератор не запустился и воткнуть источник sys_clock: hsi -> hse то все встает колом... включая отладочный интерфейс. отладчик говорит что нет связи с микросхемами. это все можно понять и даже простить ... но засада в том что как я понял нет пина BOOT0 как в stm32 и теперь невозможно запустится встроенным загрузчиком :( жопа. остается просверлить дыру в микросхеме и иголкой дать клок чтоб ее стереть. пока я не нашел выхода из этой ситуации. на форумах забугорных примерно такой же плач Жорославны о том что сожрала торт и лопнули.
2. если первое есть засада в котору не нужно вступать как в партию... то теперь наверно говнеццо которое не вынести лопатой - работа hsi и pll. ниже картинки джиттера клоков:
отстройка по времени от захваченного триггером фронта 10мкс
клок от hsi 24МГц
клок от hse 24МГц
клок от hsi->pll(x2) 48МГц
клок от hse->pll(x2) 48МГц
какие мы можем сделать предварительные выводы - hsi гамно.... ну сильно он дрожит, похоже не стали они делать аналоговый RC генератор а обошлись чем то на триггерах у которых порог не сильно стабильный. это можно понять и простить. но UART может на большой скорости и не разработать - лагать битами. нужно проверить. как и положено характеристики hse определяются в основном внешним резонатором, но наверно фазового шума тут по выше озвученной теории до хрена. модули которые не чувствительны к фазовому шуму как UART должны работать на любой скорости.
а вот pll меня убила - выход дрожит как ... слов нет без мата. из чего у меня гепотеза - а нет у них никаого ФАПЧ!!! эти мошенники тупо умножают частоту на фронтах без фапч. а пишуn в доках pll - тому что у всех в этом месте документации и микросхемы есть pll! хотя я уже не уверен что у всех в микросхеме. в доках что угодно можно писать.
то есть по предварительным наблюдениям если работать не от кварца напрямую от hse а от pll то смысла ставить кварц нету, любой говнорезанатор за 3 копейки. а по сути можно сразу hsi->pll теже яйца выйдут но без деталек.
если кто то, найдет ошибки в моих интерпретациях наблюдений я буду рад. вдруг не все так плох0 с клоками.
[обновление 2.07.23+]
интересный резултата - при работе hse->pll как мы выяснили фапч хрипит (картинка выше). как следует из документации на частоте системного клока более 24МГц нужно выставлять для флеша wait1 чтобы она успевалапроцу выдавать команды. но! ели на это положить болт и выставить wait0 то почемуто клок с выхода фапча становится намного чище!
если посмотреть на схемку rcc
меня лично напрягает что на флеш отдается 1/3 частоты от чего (в данном случае отхрипящего hsi ) это как минимум настораживает на тему а как они там синхронизацию выборки команд проца и буффера шины команд флеша делают?
все мутно но хотябы воспроизводимо на отдельно взятой микросхеме.
однакл жеж получается на 48МГц хорошего синтеза частот и временных интервалов таймерами может не получится... пока видится только от hse без фапча на 24МГц..
поробывал проверить работу на 48МГц wait0 - не начнет ли глючит? нет не начало, uart работает, тест CoreMark проходит без ошибок с укорением с 94,914 до 122,67 баллов. в таком виде riscv обходит cortex-m0plus(у которого есть инструкция умножения)
дожал! заработало LTO
memutz .././../out/image.elf 16K 2K
section info:
sec name size increase[%]
.text 14092 -1468 (-9.434450%)
.data 40 0 (0.000000%)
.bss 1016 0 (0.000000%)
utilization:
ram : 51.5625% 0 (0.000000%)
flash: 86.2549% -1468 (-9.410256%)
LTO оптимизатор пожал код почти на 10%. также вырасла скорость кода. теперь 96.06 / 124.83 балов против 94.91/122.67 без LTO, скрость увеличиласьне не много - 90% кода это сраный printf, не вычислительный код, вот его и пожал оптимизатор, к сожалению по соглашению править core_main.c нельзя. а он в нем. но размер прошивки под притык. поэтому LTO тут как нельзя здорово подходить для решения проблемы малого размера флеша.
сегодня протянул в коде таймеры, запустил.
удивительное в решете! выходы таймеров не дрожат. похоже с фапчем и клоками все в порядке, но почему mco выход такой дрожащий?
так что страхи по поводу плохих клоков временно отменяются.
[09.07.23]
обнаружена масса ошибок в рефмануале. в даташит не всматриволся. видно что копипастили возможно с v2xx или v3xx, переферия имеет добавки по сравнению с ними.
переферия dbgmcu описана в рефе неправильно и в коде pal тоже. поэтому не расчитывайте остановить таймеры при отладке если будете пользоватся фирменной библой. они на 8 бит влево промазали в регистре. регистр к моему удивлению расположен в процессоре! сsr 0x7c0.
afio exti dma tim работают пока идентично stm32fxxx, по крайней мере так выглядит, эрраты мы так пока еще и невидели, и в глубокие подвалы не заглядывали.
[14.07.2023]
написал тест измерения скорости входа выход в обработчик прерывания. вложенные прерывания и аппаратное сохранение контекста включено, обработчик собран с расчетом на это(эпилог и пролог не генерятся)
код цикла генерящего прерывания:
#include "appdefs.h"
static constexpr string_view hellow = "hellow from irq enty speed test demo!\n\n" ;
int main(void)
{
cout < cout_t::vt100_t::yellow < hellow <= cout_t::vt100_t::white ;
cout < "0. config afio exti line6 <- gpiod" <= dendl ;
afio.clock_enable();
afio.exti6_gpiod();
cout < "1. config gpiod pin5 pin6" <= dendl ;
gpiod.clock_enable();
gpiod.pin6_mode_output_50mhz();
gpiod.pin6_output_type_push_pull();
gpiod.pin6_reset();
gpiod.pin5_mode_output_50mhz();
gpiod.pin5_output_type_push_pull();
gpiod.pin5_reset();
cout < "3. config exti line6 fall edge" <= dendl ;
exti.line6_interrupt_enable();
exti.line6_rise_edge_enable();
exti.pfic_enable();
while(1)
{
gpiod.pin6_set();
while ( (gpiod.output & gpio_t::b6 )) {}
exti.line6_software_interrupt_event_generate();
while ( bool(exti.line6_interrupt_flag()) ) {}
}
}
namespace ch32v00x
{
irq_handler_hpei(exti7_0)
{
gpiod.pin5_set();
exti.line6_interrupt_flag_clear();
gpiod.pin5_reset();
gpiod.pin6_reset();
}
}
асм непоследственно цикла и обработчика
45c: /----> c88c sw a1,16(s1)
while ( (gpiod.output & gpio_t::b6 )) {}
45e: | /-> 00c4a083 lw ra,12(s1)
462: | | 0400f413 and s0,ra,64
466: | \-- fc65 bnez s0,45e <__firmware_build_time__+0x34>
468: | cb94 sw a3,16(a5)
template <typename U> __ai__ auto rd2() const { NRO return static_cast<U>(((*((volatile T*)this)) >> ((const T)U::offset)) & ((const T)U::mask)) ; }
46a: | /-> 0147a283 lw t0,20(a5)
while ( bool(exti.line6_interrupt_flag()) ) {}
46e: | | 0402f713 and a4,t0,64
472: | \-- ff65 bnez a4,46a <__firmware_build_time__+0x40>
474: \----- b7e5 j 45c <__firmware_build_time__+0x32>
00000156 <ch32v00x::exti7_0_irq_handler()>:
{
gpiod.pin5_set();
156: 400117b7 lui a5,0x40011
15a: 40078293 add t0,a5,1024 # 40011400 <__stack_end__+0x20010c00>
15e: 02000713 li a4,32
162: 00e2a823 sw a4,16(t0)
exti.line6_interrupt_flag_clear();
166: 40010337 lui t1,0x40010
16a: 40030393 add t2,t1,1024 # 40010400 <__stack_end__+0x2000fc00>
16e: 04000693 li a3,64
172: 00d3aa23 sw a3,20(t2)
gpiod.pin5_reset();
176: 00200537 lui a0,0x200
17a: 00a2a823 sw a0,16(t0)
gpiod.pin6_reset();
17e: 004005b7 lui a1,0x400
182: 00b2a823 sw a1,16(t0)
}
186: 30200073 mret
gpiod pin6 фиолетовый
gpiod pin5 желтый
mco(sys_clock) синий
чето както очено вяло. на заходе в обработчик процессор еще отмораживается на 23 такта sys_clock. в итоге порядка 480нс... есть подозрения что он сохраняет 10 "Caller Saved" регистров .. в ОЗУ
как раз по два такта на обмен по шине с ОЗУ + сохранене указателя стека. но позвольте. мы то наивно полагали что оно будет сохранять все регистры парралельно в внутрение потроха ядра за ОДИН такт...
иначе мне не вдулось это HPE китайского производства. кто то тут жаловался на 100нс, я и того в четыре раза больше получил. елочные гирлянды да, а вот моторы нет - сгорят с такой реакцийей на КЗ к херам собачьим. это пол микросекунды, а микросекнда по нашим представлениям это вечность - медь превращается в пар. пар в плазму, плазма в черныю дыру посередине изделия засасывающую в себя источник энергии.
[обновление 13.08.23]
как было написано в шапке - две платки были окирпичены выбором hse который я не включил. сегодня подал 8 мгц на pa1 - отладчик смог почистить флешь. вывод. мжно написать программу такую что оставить процессор без клока. но удалось затактировать снаружи и спасти две микросхемки.
[29.107.2023]
Внезапно чудесный dma..
протрахался вчера и сегодня... оказываеццо бит канала dma EN не сбрасывается при окончании передачи и обнулении счетчика транзакций СNTR. это происходит только при ошибке. тоесть не так как в STM32, сбрасывать нужно ручками! если не сбросить то в в сh32v307 записать новый адрес памяти не дает, а в ch32v003 дает! баги это или фичи хрен его знает - при переносе кода с 003 на 307 вылезно это потому как код не заработал... а обесчалось по документации что модули одинаковые.
одним словом не такие как у STM32 да еще промеж себя отличаются. все это не криминально, но из док этого нифига не видно. эрраты нет. приходится на пузе проползать по всему этому болотцу. но в итоге работает. дальше поробую поднять usb и сеть. посмотрим сколько мозка этот эксперимент высосет. сегодняшний результат - экранчики oled 0.96 (ssd1306) подро работают на 1.23МГц I2C параллелно с eeprom FT24C64A. время обновления картинки 8.3мс. отрисованный фрембуфер качается в ssd1306 асихронно по средством dma.
[09.01.2024] обнаружил новое чудесатое
в обоих микроконтроллерах v003(ядро qkv2a) и v307(ядро qkv4f) в ядрах реализован системный таймер SysTick, причем в qkv2a каcтрированый по разрядности. так вот, в ядрах есть специальный регистр dcsr(адрес 0x7b0) в котором девятый бит включает останов таймера при отанове процессорав во время отладки. так вот в v307 это работает, в v003 не работает, ошибка в ревизии микросхемы или что еще, не знаю но в дока для обоих написано rw