Комментарии 61
По идее (раз скорость режет ожидание flash), «полноценных» 48МГц можно достичь при исполнении из RAM. Применимость в целом так себе, но в каких-то случаях вынести туда критический код может и помочь.
Проверять не хочу, но там может вылезти другая проблема - Гарвардская архитектура. Сейчас шины должны работать в параллель. Флэшка для кода, ОЗУ для данных. Так - будут задержки из-за двойного использования шины ОЗУ для кода/данных.
Но я проверял только те режимы, которыми собираюсь пользоваться, а уж проверку этого дела оставим тем, кто решит использовать этот вариант. Но на Cortex M при исполнении из ОЗУ этот эффект точно возникает.
Другой вопрос, как быть с предсказателями ветвлений, если они не могут предсказать безусловный переход?
Подскажите, осциллограммы строились на первой итерации цикла или нет? По идее, на первой итерации у предсказателя должна быть осечка, ведь он ещё не встречался с этим циклом, ещё не обучен.
Уже на устоявшемся цикле. Сначала стартовал код, потом я выставлял картинку поэффектнее, уже затем останавливал развёртку осциллографа.
А вот тут ещё вопрос, что вообще имеется в виду под предсказателем в данной реализации? А то при общей невнятности документации может оказаться простым «все переходы назад - вероятны, вперёд - нет».
На скрине в статье видны BTB, BHT, RAS -- это механизмы Branch Target Buffer, Branch History Table и Return Address Stack. Конкретно в этом куске кода наверно отыграет branch target buffer -- фактически маленькая ассоциативная память (кэшик), которая по program counter выдаёт сохраненный ранее адрес перехода.
А на сколько маленькая? Не подскажите, где лучше почитать про эти механизмы?
Можно почитать например у Хеннесси & Паттерсона в "Computer Architecture: A Quantitative Approach" или у Шена & Липасти в "Modern Processor Design", обычно эти темы есть в книгах по архитектуре компьютеров.
Насколько маленькая, точно не знаю, обычно это компромисс по ресурсам и получаемому выигрышу в производительности. Для больших производительных процессоров она на пару тысяч записей, для микроконтроллера -- возможно сколько позволяет бюджет, может 16 записей.
Полезная статья. Надо будет свой msp430fr5939 протестировать (да, там не заявлено прямым текстом об "улучшайзерах", но мало ли).
Крутая и глубокая статья, спасибо. Столько интересной информации из простого ногодрыга👍👍
Стало крайне интересно как будет работать ch32l103
Ядро и память те же, а вот частота 96 МГц. Возможно и вам он будет интереснее, по цене схожи
Очевидно, что доступ к flash памяти вызывает простой конвейера. Чтобы нивилировать тормознутость флэша в ядро добавляют кэш инструкций. Есть ли такой кэш в этом китайском МК и какого объема ?
Я работаю со своей синтезируемой СнК (RV32IMFAC). Частота ядра - 60 МГц. Внутренняя шина - AXI4. При исполнении программы из внешней NOR Flash памяти (QSPI) производительность падает на 30%, хотя доступ к памяти в 4 раза медленее и возможен только блоком. I$ размером всего 2 кБ весьма неплохо "сглаживает" тормознутость флэша.
Есть ли такой кэш в этом китайском МК и какого объема ?
Статья начинается с цитат из документации, из которых следует, что такого кэша там нет. Правда, знаем мы эти китайские документы... Но кажется, тут не обманули (а жалко)
Ну тогда о чем речь ? Можно расходиться.
PS: Даже в минималистичном отечественном MIK32 есть кэш для инструкций.
Даже в минималистичном отечественном MIK32 есть кэш для инструкций.
Если я ничего не путаю, он там не от хорошей жизни, а чтобы из SPI флэшки код исполнять, ибо со встроенной флэшкой напряжёнка (всего 8К). А так - в нём даже нормального контроллера прерываний, судя по документации, нет (так и написано - "отключён").
Кэш инструкций нужен всегда. Без него не будет работать конвейер без остановок.
Flash памяти в MIK32 нет вообще, там есть EEPROM размером 8КБ, в которую предполагается размещать "загрузчик".
Контроллер прерываний в MIK32 есть, их там два - один из них, тот что идет в составе ядра, отключен за ненадобностью и вместо него работает внешний.
Контроллер прерываний в MIK32 есть, их там два - один из них, тот что идет в составе ядра, отключен за ненадобностью и вместо него работает внешний.
Где можно почитать про него? В штатном документе ничего не понятно. Ну, кроме слова "отключён" и того, что нужного раздела про второй контроллер прерываний я не нашёл в оглавлении. Вот сейчас пробежался. Может, глубоко спрятано. Буду рад найти и ознакомиться.
Flash памяти в MIK32 нет вообще, там есть EEPROM размером 8КБ, в которую предполагается размещать "загрузчик".
То есть, код будет исполняться из медленной внешней SPI флэшки (спасибо, что QSPI). Ещё не ясно, что лучше, а что хуже. Причём я не защищаю китайское решение. Я выпадаю в осадок от нашего.
Правда, знающие люди мне сообщали, что всё это не потому, что разработчики странные, а просто по техническим причинам сделано. Но мне, как пользователю, от этого всё равно не легче. И случись выбирать, делать решение на кэшируемой системе с QSPI или некэшируемой, но с внутренним флэшем - я сначала проведу опыты, а потом уже выберу.
В исполнении кода из QSPI нет ничего зазорного, это типовое решение. Типичный пример - изделия серии ESP32, которые работают на достаточно высокой частоте ядра (120-240 МГц) и исполняют код прямо из NOR фэл памяти (XiP) без тормозов.
На счет контроллера перываний в MIK32 - посмотрите в примеры из SDK. Вот кусочек HAL для настройки прерываний, он рабочий. Внешний контроллер называется EPIC. Внутренний (PLIC) по какой-то причине не задействован. Причина мне не известна, но я подозреваю что это как-то связано с архитектурой внутренней шины на которой висит вся аппаратура - проблема совместимости.
PLIC является частью спецификации RISC-V. В моей синетзируемой системе его тоже нет, а вместо него я написал свой контроллер, более удобный для моих задач.
Вот кусочек HAL для настройки прерываний, он рабочий
Посмотрел. Не нашёл векторов.
изделия серии ESP32, которые работают на достаточно высокой частоте ядра (120-240 МГц)
Да, но насколько я знаю, у АМУРа частота всего 32 МГц, а это совсем другое.
Посмотрел. Не нашёл векторов.
У RISC процессоров убычно всего один вектор. Разруливание по источнику прерывания делается программно, такова идеология RISC - минимум железа.
Да, но насколько я знаю, у АМУРа частота всего 32 МГц, а это совсем другое.
ESP32 был приведен для примера чтобы показать, что даже из медленной flash памяти вполне возможно нормальное исполнение программы на больших тактовых частотах если есть достаточное количество кэша. Почему в CH32 разработчики не завезли кэш ? Наверное сильно экономили на площади кристалла. Видимо предполагается, что критическая часть программы должна исполняться из RAM и программист это понимает.
У RISC процессоров убычно всего один вектор. Разруливание по источнику прерывания делается программно, такова идеология RISC - минимум железа.
Мы здесь не комиссии доказываем, что отечественный контроллер надо принять, а как программисты общаемся. Так что давайте смотреть правде в глаза. Если прерывание требует много программного разбора - зачем оно нужно? Тогда и опроса хватит. Или, скажем, DMA. Прерывания были придуманы для БЫСТРОЙ реакции на событие. Источников событий может быть множество.
Если не хватает транзисторов на кристалле - ну хоть программистам не рассказывайте, что это в их же интересах и что так принято. Это члены комиссии могут не иметь опыта работы с другими системами, а программисты - имеют.
И я надеюсь, Вы про RISC-V сейчас говорите. Иначе как у вполне себе RISCового Cortex M NVIC является неотъемлемой частью, начиная с M0+? Да и у других ARMов прерывания нормальные, с векторами. AVR - они RISC? У них вектора. Мало того, в спецификации RISC-V заложены вектора хоть с адресами, хоть с JUMPами (и таки да, совсем без векторов допустимо, но именно ДОПУСТИМО).
Идея RISC состоит в более целесообразном использовании транзисторов. Опыт показал, что высвободившиеся "транзисторы" (на самом деле площадь на кристалле) гораздо эффективнее отдать под кэш.
Не хотелось бы в сотый раз расчихлять эту тему, но ARM-ы уже давно не являются RISC процессорами.
Наличие множества векторов не позволяет обработать прерывание более эффективно, просто часть обработки спрятана "под капот". Но эта работа (выраженная в затраченных тактах процессора) все равно происходит. Так зачем её прятать ? Более того, когда в машине всего один вектор, то программа один раз сохраняет состояние контекста, а когда несколько - то делает это для всех векторов, что зря тратит драгоценную память и засоряет кэш. Для таких приложений как МК, где каждый байт на счету, это очень важно!
В RISC-V спецификации определены два вектора: timer interrupt и exception+external interrupt. Также есть пара десятков различных исключений (exception cause). Срабатывание этих исключений может происходить как по одному вектору, адрес которого находится в mtvec, так и по адресу mtvec+4*mcause. Режим работы определяется состоянием младших двух битов в mtvec. Но, это совершенно не те вектора которые Вы хотите видеть - это не вектора прерываний от внешней аппаратуры, это вутренние исключения формируемые ядром в процессе исполнения инструкций. Все внешние прерывания в RISC-V это один вектор - exception c установленным флагом external interrupt. Теоритически, разработчик ядра может расширить список mcause и добавить туда свои "причины" как источники внешних прерываний, но я не помню чтобы такое решение допускалось спецификацией.
AVR8 это своя самобытная архитектура - эдакий гибрид ежа и ужа. Её разработчики посчитали, что сделать отдельный вектор на каждый источник это удобно, ну так вот им захотелось. Однако учитывая, что на многих МК младших серий ATmega/ATtiny памяти катастрофически мало, то отдавать драгоценные байты под таблицу прерываний это просто кощунство! :) Еще раз, каждая процедура обработки прерывания должна сохранить контекст, а это куча дублирующего кода!
Идея RISC состоит в более целесообразном использовании транзисторов. Опыт показал, что высвободившиеся "транзисторы" (на самом деле площадь на кристалле) гораздо эффективнее отдать под кэш.
У ESP8266 этого кэша было 80К. Насколько я знаю, у ESP32 его сотни килобайт (сам не пользовался, так что не буду цифрами оперировать). У Амура - 256 слов (1 килобайт). Да там сплошные промахи будут! А что бывает от промахов, я описывал тут https://habr.com/ru/articles/467353/ (повторяемость времянок просто никакая). Для систем с большим кэшем - бесспорно, всё классно. Но при чём тут Амур?
Не хотелось бы в сотый раз расчихлять эту тему, но ARM-ы уже давно не являются RISC процессорами.
Один такт на команду. Малые ресурсы для M0 и M3. Чем не RISC?
Про один вектор я не буду цитировать. Оверквотинг будет. Скажу только, что я тоже умею маскировать "ну мы иначе не можем" под "это же круто", когда надо доказать что-то комиссии, которая не понимает. Но мы-то тут понимаем... NVIC и его наследники прекрасно сохраняют контекст один раз, потом ставят прерывания в очередь, восстанавливая контекст после последнего. Смотрите описание ядра, ссылка в начале статьи. Там даже картинки есть, что не характерно для китайских описаний.
На тему "может добавить свои причины" - как это добавить у Амура? А что в принципе может добавить - ну это бесспорно. Только зачем Вы приводите в пример контроллер, которому это, кажется, не под силу? Либо покажиие обратное. Заметьте, я про него только отвечаю. Сам первый ничего не пишу. Но мы тут столько уже нашли, что глянь на нашу переписку тот, кто принимает решения... В общем, я не советую тут его в пример ставить... Не стоит он того. А все наши слова потом могут быть прочитаны теми, кто может прийти к Микроновцам и начать задавать вопросики.
Еще раз, каждая процедура обработки прерывания должна сохранить контекст, а это куча дублирующего кода!
Читайте описание ядра по ссылке в начале статьи. Ничего не надо сохранять, всё сохраняет аппаратура! Там даже есть специальный атрибут:
__attribute__((interrupt("WCH-Interrupt-fast")))
В результате, в конце будет команда выхода из прерывания, но регистры сохраняться не будут.
А так, всё, что Вы пишете - это про синтезируемые поделки. Если делаются синтезируемые ядра - там можно развлекаться под конкретный случай. Правда, во времена, когда я ими интересовался, Fmax был у них смешной. А вот чипы общего применения делать... Ну сделали что-то пещерного уровня, так сидите тихонько! Выставлять этот позор, как пример для остальных... Ну получите обоснованную критику. Распоследнейшие китайцы делают более логично! А в синтезированных ядрах - да выкидывайте всё, что не нужно в текущем проекте, так даже правильней. Каждому проекту свой набор железа!
Но в ширпотребовских чипах должно быть всё, что нужно среднестатистичекскому программисту. И не надо рассказывать, что "вам от того, что вы на 32 МГц будете долго-долго перебирать биты статуса разных блоков, будет только лучше".
Читайте описание ядра по ссылке в начале статьи. Ничего не надо сохранять, всё сохраняет аппаратура! Там даже есть специальный атрибут:
attribute((interrupt("WCH-Interrupt-fast")))
В результате, в конце будет команда выхода из прерывания, но регистры сохраняться не будут.
А можно вот про этот момент поподробнее, желательно с куском результирующего ассемблерного кода. Как это вообще стыкуется со спецификацией RISC-V ? Помоему это какая-то отсебятина от производителя этого МК.
Мои позорные поделки можете оценить сами. На ПЛИСине в 25К ячеек и стоимостью где-то между CH32F103 и STM32F103, легко достигается Fmax=60 МГц с кучкой полезной аппаратуры, блоком MMU, MAC/RMII и видеоадаптером с выводом на HDMI. Встает вопрос - чем CH32 лучше чем моя синтезированная СнК кроме цены микросхемы, которая вообще не является определеющей в стоимости изделия ? При этом на своей СнК я могу в любой момент добавить требуемой аппаратуры или убрать ненужную и пустить высвободившиеся ресурсы под тот же кэш (и даже увеличить Fmax если убрать MMU).
На счет позорного MIK32 тоже не соглашусь. На мой взгляд изделие вполне пригодное для производственных задач. Но тут важен вопрос собственного производства, которое пока что другого на гора выдать не может чисто физически. Так что надо радоваться тому что есть и стараться использовать там где это применимо.
Насчёт ПЛИС - надо смотреть конкретное семейство. На Альтере и Латтисе один и тот же исходник даёт ох какой разный FMax. Потому что у Альтеры отличные межбюлочные связи, а Латтис гонит всё через общую коммутационную матрицу.
Мы делали RGMII на Латтисах, выше 80 МГц подняться не удавалось, но это без процессорного ядра. Как его добавляли - FMax проваливалась. Разумеется, мы добавляли ядра из поставки Litex или как там его. Очень давно дело было.
На Альтерах то же RGMII ядро синтезировалось с очень хорошим параметром FMAx, а вот Litex не любой Циклон тянул. ОЗУ маловато у десятых Циклонов оказалось.
Чем CH32 лучше синтезированного? Для спецухи - да, цена не важна. Для ширпотреба - фирма Fujitsu провалилась на рынке с семейством дисков MPG, когда применила там технологию, которая снижала цену производства на какие-то там десятки центов, насколько я помню. Там, правда, был комплексный подход к причинам провала, но они ради десятков центов разницы на новую технологию пошли, которая среди прочих, им повредила. В ширпотребе важна каждая копейка. Особенно если есть конкуренты.
У нас на заводе в конце девяностых ребята проектировали "народный телевизор". Так чтобы уложиться в спущенную сверху цену, под конец резисторы из схемы выкидывали.
Так что надо радоваться тому что есть и стараться использовать там где это применимо.
Но не ставить его в пример. Тихонечко радоваться. Со слезами на глазах, что даже китайские друзья на пять кругов впереди. И стараться прививать всем любовь к прекрасному, чтобы они это прекрасное спроектировали, а не почивали на лаврах ближайшие 20 лет...
Мы делали RGMII на Латтисах, выше 80 МГц подняться не удавалось, но это без процессорного ядра. Как его добавляли - FMax проваливалась. Разумеется, мы добавляли ядра из поставки Litex или как там его. Очень давно дело было.
Для RGMII требуется 125МГц на сколько я помню. Я думаю, что на Lattice ECP5 8-го грейда это вполне достижимо. У меня HDMI/DVI тактируется на 250МГц.
Litex это дурацкая обертка/надстройка над VexRiscv для тех, кто не знает SpinalHDL. ;-) Я взял оригинальный VexRiscv и начал над ним работать. Дописал много всякой аппаратуры, закастомайзил ядро, добавил кэша. Максимальная частота голого ядра на Lattice ECP5 7-го грейда - 85 МГц. Со всем мои обвесом - 61 МГц. Считаю, что как микроконтроллерное решение общего назначения - более чем. Сейчас пытаюсь заинтересовать студентов этой темой.
А можно вот про этот момент поподробнее, желательно с куском результирующего ассемблерного кода. Как это вообще стыкуется со спецификацией RISC-V ? Помоему это какая-то отсебятина от производителя этого МК.
Ну да, отсебятина... Но работает же! Я же говорю, скачайте описание ядра по ссылке в начале статьи, там всё есть!
Ну давайте я набросаю чисто от балды. Я же этот процессор только начал изучать... К концу семестра стану умнее, пока студентов учить буду
void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
volatile uint32_t tickCnt = 0;
void SysTick_Handler(void)
{
tickCnt += 1;
}
Получаем
0000063c <SysTick_Handler>:
63c: 82818793 addi a5,gp,-2008 # 200000a8 <_edata>
640: 4398 lw a4,0(a5)
642: 0705 addi a4,a4,1
644: c398 sw a4,0(a5)
646: 30200073 mret
Почему - см. документы, ссылка в начале статьи...
По Вашей ссылке я скачал какой-то CH32X035DS0.PDF на 42 страницы, там кроме общих слов ничего нет.
Хотелось бы понять как работает эта фича: какие регистры сохраняются, куда они сохраняются и сколько тактов это занимает. Подозреваю, что сохранение регистров происходит не на стек, а в копию регистрового файла. Иначе это уже нифига на "fast".
Я же сказал, качайте описание ядра.
https://www.wch-ic.com/downloads/QingKeV4_Processor_Manual_PDF.html
Там, правда, сказано:
The V4 series microprocessors support hardware single cycle automatic saving of 16 of the shaped Caller Saved registers to an internal stack area that is not visible to the user. When an exception or interrupt returns, the hardware single cycle automatically restores data from the internal stack area to the 16 shaped registers. The hardware stack supports nesting with a maximum nesting depth of 3 levels. After a hardware stack overflow, if a higher priority interrupt is still allowed to execute, the "field" is saved to the user stack area.
То есть, куда-то во внутреннюю сущность, не в память. А куда - не скажут... Если внутренняя сущность переполнена - вроде, начнут в ОЗУ сохранять.
Интересное решение. Странно только по чему сохраняют не все регистры, а только 16 ? Компилятору надо быть осторожным при сборке обработчика прерываний, чтобы какая нибудь из вызванных из него функций не испортила несохраненные регистры. Скорее всего им пришлось компилятор изрядно пропахать.
По той же причине, почему не все регистры сохраняет NVIC. Есть Call Convention, которая прописывает, какие регистры функция может портить, а какие должна сохранить. У них же даже псевдонимы начинаются на a, t, s (Argument, Temporary, Saved). Вот которые функция в любом случае обязана сама сохранять - зачем париться? Она их сохранит даже при обычном вызове. Так положено!
У NVIC всё то же самое. Всё завязано на Call Convention. Так договорились, дальше даже аппаратура блюдёт эти договорённости. Но там до префиксов в псевдонимах не дошли. Тут - дошли.
Есть Call Convention, которая прописывает, какие регистры функция может портить, а какие должна сохранить
Эта конвенция работает только для основного кода. С перываниями всё серьезней. Функция обработчика прерывания может быть вызвана в любой момент и все регистры которые она изменяет, она обязана сохранить и восстановить их прежние значения перед возвратом в основной код. Если функция обработчик что-то позабыла сохранить (понадеявшись на NVIC, который сохраняет только 16 регистров), то функция пользователя вызванная из обработчика об этом никак не узнает и рискует испортить например временные (t) регистры, ведь согласно конвенции она не обязана их сохранять. Тогда обрабочик закончится и вернется в основной код с испорченными регистрами. Интересно как это дело обруливается. Я сомниваюсь, что китайцы на столько умны, чтобы так круто пропахать компилятор с целью заставить его анализировать вызов функций из обработчика прерываний и делать неявное сохранение того, что еще не сохранилось.
Ради интереса, добавьте в SysTick_Handler
вызов своей функции, а из неё вызов библиотечной puts(). Очень интересно как будет выглядеть ассемблер.
А можно вот про этот момент поподробнее, желательно с куском результирующего ассемблерного кода. Как это вообще стыкуется со спецификацией RISC-V ?
Ну, немного про это есть у меня: https://habr.com/ru/articles/866798/
А по существу - с risc-v это не стыкуется никак и сделано WCH-ами через жуткие костыли. Они аппаратно сохраняют временные регистры в одном из 2-4 специальных стеков. Соответственно, обработчик прерывания может выглядеть как обычная функция (естественно, a*, s* регистры он все еще должен сохранять самостоятельно).
Там даже есть специальный атрибут:
__attribute__((interrupt("WCH-Interrupt-fast")))
Не забывайте, это WCH-специфика. Которая, к тому же, до сих пор нормально не поддерживается gcc.
вы на 32 МГц будете долго-долго перебирать биты статуса
О да, на целых полтора такта больше (ручной переход по таблице по номеру из mcause.
О да, на целых полтора такта больше (ручной переход по таблице по номеру из mcause.
А он там есть? Из документации не ясно, исходники HAL я скачал, по слову cause в них ничего не ищется. И в документации сказано, что контроллер прерываний отключён.
Не то, чтобы я утверждаю, что его нет... Просто искал - не нашёл. Зато нашёл вот такое... О чём я и говорил... Причём когда говорил - я не знал, что оно найдётся
#define EPIC_CHECK_TIMER32_0() (EPIC->RAW_STATUS & (1 << EPIC_TIMER32_0_INDEX))
#define EPIC_CHECK_UART_0() (EPIC->RAW_STATUS & (1 << EPIC_UART_0_INDEX))
#define EPIC_CHECK_UART_1() (EPIC->RAW_STATUS & (1 << EPIC_UART_1_INDEX))
#define EPIC_CHECK_SPI_0() (EPIC->RAW_STATUS & (1 << EPIC_SPI_0_INDEX))
#define EPIC_CHECK_SPI_1() (EPIC->RAW_STATUS & (1 << EPIC_SPI_1_INDEX))
#define EPIC_CHECK_GPIO_IRQ() (EPIC->RAW_STATUS & (1 << EPIC_GPIO_IRQ_INDEX))
#define EPIC_CHECK_I2C_0() (EPIC->RAW_STATUS & (1 << EPIC_I2C_0_INDEX))
#define EPIC_CHECK_I2C_1() (EPIC->RAW_STATUS & (1 << EPIC_I2C_1_INDEX))
#define EPIC_CHECK_WDT() (EPIC->STATUS & (1 << EPIC_WDT_INDEX))
#define EPIC_CHECK_TIMER16_0() (EPIC->RAW_STATUS & (1 << EPIC_TIMER16_0_INDEX))
#define EPIC_CHECK_TIMER16_1() (EPIC->RAW_STATUS & (1 << EPIC_TIMER16_1_INDEX))
#define EPIC_CHECK_TIMER16_2() (EPIC->RAW_STATUS & (1 << EPIC_TIMER16_2_INDEX))
Кто есть, стандартный CSR-регистр mcause? Есть, конечно. Вот пример использования в прерывании: https://github.com/KarakatitsaRISCV/riscv-asm/blob/main/4.interrupt_ch32/src/main_1_unified.S#L211
Я про Амур. Что в CH32 всё есть - это даже не обсуждается. В ветке шла речь про то, что АМУРа с его невекторизированными прерываниями любой китаец уделает.
Есть ли соответствующий функционал в Амуре? Учитывая вот этот текст из документации:
4) встроенный интегрированный программируемый контроллер прерываний отключен;
и полное отсутствие слова cause как в документе, так и в исходниках HAL
Но как это делается в правильных системах - спасибо за информацию. Намотал на ус. Правда, решение от ch32 с их аппаратным стеком, переходящим при острой необходимости в аппаратное сохранение в ОЗУ - ещё правильнее для контроллеров.
Про Амур не знаю. У меня его нет, соответственно и не интересовался.
Правда, решение от ch32 с их аппаратным стеком, переходящим при острой необходимости в аппаратное сохранение в ОЗУ
Стоп. Я сейчас попытался найти куда же оно сохраняет. Похоже, в ядре V2 на юзерский стек, а V3, V4 - куда-то в недра, такое ощущение что вообще без возможности ручного доступа.
а V3, V4 - куда-то в недра, такое ощущение что вообще без возможности ручного доступа.
Для V4 они обещают сохранять 3 элемента именно в секретные недра, но зато за один такт все регистры. Когда эти недра будут переполнены - четвёртое сохранение уже пойдёт в ОЗУ. Будет ли пятое - они не говорят. Припрёт - буду проверять. Для учебных целей и одного достаточно :-).
Кстати, я проверил. Один там аппаратный стек у ch32. Это есть ещё вектора, которые можно брать, не тратя такты на память. А стек - он ко всему FPIC относится. И у одного стека три элемента. При переполнении, сохранение начнёт идти в ОЗУ, то есть, медленнее.
Методика проверки:
Заводим обработчик прерывания:
void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
volatile uint32_t tickCnt = 0;
void SysTick_Handler(void)
{
SysTick->SR = 0;
tickCnt += 1;
}
Инициализацию копируем штатную, никаких спецвекторов, всё через общую таблицу:
void SYSTICK_Init_Config(u64 ticks)
{
SysTick->SR = 0;
SysTick->CNT = 0;
SysTick->CMP = ticks;
SysTick->CTLR =0xF;
NVIC_SetPriority(SysTicK_IRQn, 15);
NVIC_EnableIRQ(SysTicK_IRQn);
}
Ну, и в функцию main() вставляем:
SYSTICK_Init_Config(SystemCoreClock/1000-1);
while(1)
{
__NOP();
}
Смотрим, во что превратился обработчик прерываний:
0000067c <SysTick_Handler>:
67c: e000f7b7 lui a5,0xe000f
680: 0007a223 sw zero,4(a5) # e000f004 <_eusrstack+0xc000a004>
684: 82818793 addi a5,gp,-2008 # 200000a8 <_edata>
688: 4398 lw a4,0(a5)
68a: 0705 addi a4,a4,1
68c: c398 sw a4,0(a5)
68e: 30200073 mret
Запускаем. Периодически ставим точку останова на nop. Убеждаемся, что tickCnt увеличивается, а регистр a5 не изменяется. Ради интереса я во время одной из остановок туда вообще 0x12345678 вписал. И он таким и остался. Значит, при входе в обработчик прерывания через общую таблицу, всё сохраняется в спецстеке. Что соответствует рисунку из описания ядра, просто хотелось в этом убедиться.
То есть PFIC_VTFIDR и PFIC_VTFADDRR никак не связаны? Надо будет проверить, а то, возможно, я в своей статье дезынформировал.
Я так понял, эта парочка как раз связана между собой. Но они используются для того, чтобы четыре вектора можно было вызывать вообще не обращаясь к ОЗУ (не тратя такты на чтение).
А тот самый знаменитый стек для сохранения регистров, к ним никак не привязан. Он работает и для тех, кто через неё вызван, и для обычных. Мой тест показал, что для тех, кто вызван через основную таблицу, стек работает.
Всё это не противоречит рисунку из раздела Vector Table Free (VTF) . Эта парочка - просто ещё один элемент ускорения.
Ага, нашёл детали про этот стек. Вот описание бита HWSTKOVEN:
Note: HPE depth is 3. When the configuration nesting level is greater than 3, if the bit is set to 1, the low priority three interrupts need to be configured as HPE and the high priority as SPE.
Судя по всему, там не автоматика перейдёт на другой метод сохранения, а надо на аппаратный режим только три самых высокоприоритетных уровня посадить. Либо сбросить HWSTKOVEN. Тогда прерывания будут заблокированы, пока место в стеке не появится.
Мне проще. У моего процессора глубина стека два, и уровней приоритетов - тоже два.
Вот чего не могу найти - как они с порчей mepc борются при входе во вложенное прерывание. На рисунке он не сохраняется в аппаратном стеке. И про штатную идею "При входе в обработчик, новые прерывания запрещены, сначала сохраните mepc и mstatus, а уже потом разрешайте прерывания" у них написано:
MIE is the global interrupt enable bit, and when entering the exception or interrupt, the value of MPIE is updated to the value of MIE, and it should be noted that in the QingKe V4 series microprocessors, MIE will not be updated to 0 before the last level of nested interrupts to ensure that the interrupt nesting in machine mode continues to be executed.
Что-то тут не так, но что? Надеюсь, рисунок. Как доберусь до железа - проверю.
Мы здесь не комиссии доказываем, что отечественный контроллер надо принять, а как программисты общаемся. Так что давайте смотреть правде в глаза. Если прерывание требует много программного разбора - зачем оно нужно?
Потому что это стандарт ядра RISC-V, которое изначально отнюдь не для контроллеров проектировалось. А те, кто захотел его адаптировать под контроллеры, городят свои расширения: ECLIC, PFIC, ... Правда, обычно все же сохраняют совместимость.
ESP32 был приведен для примера чтобы показать, что даже из медленной flash памяти вполне возможно нормальное исполнение программы на больших тактовых частотах если есть достаточное количество кэша.
Причём сказано это было в защиту идеологии, применённой в Амуре. Только там частоты не те, и объём кэша ещё сравнить надо. Что хорошо в ESP32, тем не стоит гордиться в более слабых системах
Наверное сильно экономили на площади кристалла.
Так экономили, что разместили там 60К флэша, нормальный контроллер прерываний и ещё сопроцессор PIOC с собственным RISC-ядром. И ещё атомарные команды имеются в основном ядре. Всем бы так экономить! И стоит это 150 рублей с макеткой. У них всё на 24 МГц работает нормально, а Амур на его 32МГц из QSPI будет только подгружать всё на 16МГц.
Хотя, что нет кэша - мне не нравится. Но у Амура кроме кэша толком и нет ничего, а кэш там скорее чтобы компенсировать отсутствие флэшки встроенной.
У MIK32 АМУР нет проблем с исполнением программы из внешней NOR Flash, там производительность падает на 10-15% относительно того же кода в RAM или в EEPROM. Но не в два раза! Я привел его для сравнения чтобы показать, что при наличии кэша (и мозгов у разработчика) XiP исполнение не является проблемой.
Flash-а в MIK32 АМУР нету по причине того, что техпроцесс на Микроне не позволяет его изготавливать. А вот почему Микрон пожадничал и не сделал EEPROM хотя бы 32КБ - вот это большой вопрос. Для тех приложений, на которые расчитан MIK32, внешний flash большого обьема - как собаке пятая нога. Почти всё что на нём можно сделать легко умещается 16-20КБ кода. У меня был опыт по уталкиванию в 8КБ EEPROM кода для захвата данных с АЦП, работой с внешним датчиком по I2C, ведения расчета и выдачи результата через Modbus/RTU и токовый ЦАП 4-20ма . То есть обошлись без внешнего flash-а совсем! Для этого пришлось исключить тяжеловесный HAL от производителя и самостоятельно работать с регистрами.
А вот почему Микрон пожадничал и не сделал EEPROM хотя бы 32КБ - вот это большой вопрос.
За что купил, за то и продаю, но знающие люди сказали, что там шли большие споры, отдать транзисторы под ОЗУ или под ПЗУ. Короче, Тришкин кафтан. Отдали под ОЗУ.
Сделали, что могли. Старались, как могли. Но не надо ЭТО выдавать, как пример для других, и всё будет хорошо. Компромиссное решение. И вот пусть тихонечко лежит и используется там, где нужна импортонезависимость. А как пример - это плохой пример. Тем более, что SPI флэшка нужна тоже импортная.
Внутренний (PLIC) по какой-то причине не задействован.
Так все равно им никто не пользуется. Даже там, где реализован - gd32, ch32. В контроллерах единственный вектор на все прерывания это неудобно.
В контроллере из статьи стоит PFIC. У него ноги растут из NVIC, даже в api имеется строчка
#define NVIC PFIC
А вообще, там поддерживаются оба вида векторов - хоть с адресами переходов (по умолчанию API под них заточено), хоть с таблицей из JUMPов. При этом даже есть аппаратный стек сохранения регистров на два комплекта (не завязанный на память). Но только на два. Больше не сохранит.
А для пущего веселья, в PFIC имеются четыре сверхбыстрых вектора. На них можно переназначить любые штатные. Они отличаются тем, что адрес обработчика не читается из ОЗУ, а хранится во внутренних регистрах, так что для обращения к нему не нужны лишние такты.
В контроллерах единственный вектор на все прерывания это неудобно.
А кто сказал, что в RISC машинах должно быть что-то удобное для программиста ? В RISC действует принцип минимальной достаточности - одного вектора вполне достаточно, дальше всё разруливается программно на уровне HAL библиотек.
И кстати, по мне так на оборот - очень удобно. Для сохранения контекста достаточно сохранить SP, GP, PC, один вектор прерывания и пару регистров состояния PLIC. А вот когда векторов много и у каждого вектора свои настройки - вот это беда.
А кто сказал, что в RISC машинах должно быть что-то удобное для программиста ?
Разработчики ARM ядер сказали. При том, что Cortex M0 и даже Cortex M3 весьма не требовательны к кристаллу.
А с таким подходом только не рыночными методами можно продавать контроллеры, имея конкурентов в лице ARM.
Да и даже в лице китайцев с удобными RISC-V.
Разработчики ARM ядер сказали.
А они у Паттерсона с Хеннесси разрешения спросили чтобы называться RISC ? ;-)
Да и даже в лице китайцев с удобными RISC-V.
Ну тут как, либо Вы пользуетесь "удобным китайским RISC-V" у которого конвейер пробуксовывает при работе с flash и аппаратура "как бы совместима с STM32" но чутка не до конца, либо пользуетесь страшным и ужасным MIK32 лишенным векторных прерываний и еще много чего "всякого вкусного", который просто выполняет свою маленькую задачу. :-)
Но. Я боюсь, что уже не далёк тот день, когда не окажется даже китайских МК. Так, что рекомендую привыкать к минимализму во всём и к тому что есть. А из своего у нас есть пока что только MIK32 АМУР.
Но. Я боюсь, что уже не далёк тот день, когда не окажется даже китайских МК. Так, что рекомендую привыкать к минимализму во всём и к тому что есть. А из своего у нас есть пока что только MIK32 АМУР.
Странный подход. Вместо того, чтобы стремиться подтягивать аппаратуру к более высоким стандартам, говорить, что все будем работать на чём-то странном. Лично я тогда за PDP-11. Ну, в современном её исполнении. Я ещё даже некоторые команды наизусть помню.
Смею Вас успокоить. Если не окажется даже китайских процов, все Амуры будут зафрактованы военными заводами. С имеющимися мощностями производства, их даже для производства спецухи не будет хватать. Так что у нас их не будет. Может, я по блату и смогу добыть пару Миландровских Cortexов. Но это не точно.
Так что даёшь новые семейства, в которых учтены минимально необходимые вещи! И мы, программисты, должны указать разработчикам на то, без чего лучше не проектировать!
Значит... Кэш команд, кэш данных, векторный контроллер прерываний с автосохранением контекста... Что там ещё? Про DMA пусть не забывают... Ну, и прочие вкусности. А! Чтобы наша дискуссия не была оффтопиком - пусть проверяют на отсутствие того бага, который в статье был выявлен! С чтением данных до того, как были выставлены новые.
Странный подход. Вместо того, чтобы стремиться подтягивать аппаратуру к более высоким стандартам, говорить, что все будем работать на чём-то странном.
А кто сказал, что прогресс на этом MIK32 должен остановиться ?
МИЭТ прошлой весной выпустил свой студенческий "hackee" на тех же мощностях Микрона, но там и частота 85 МГц и ОЗУ 128кБ. Я уверен, что Микрон через год выпустит какой нибудь MIK32 2.0 нашпигованный аппартурой под самые нибалуй и частотой под 200 МГц. Но вот векторный контроллер прерываний не нужен! И автосохранение регистров тоже не нужно. :)
Лично я тогда за PDP-11. Ну, в современном её исполнении. Я ещё даже некоторые команды наизусть помню.
Ктож запрещает ? Н1836ВМ3 - 16 МГц, до 4МБ адресуемой памяти, MMU (Unix запустить можно!). Правда не уверен что их можно приобрести.
А кто сказал, что прогресс на этом MIK32 должен остановиться ?
Вы сказали :-) Вот:
Так, что рекомендую привыкать к минимализму во всём и к тому что есть.
И баннер "Приходите и покупайте АМУР" мне свалился весной 22-го. Когда его стало возможно купить? Новых баннеров мне пока не сваливалось.
Но я буду рад. Но если мне будут текущий АМУР ставить в пример - буду доказывать, что это компромиссное решение, от безысходности... А не потому, что он хорош. Не надо его в пример ставить. Не может он полноценным контроллером называться. Копеечные китайские намного дальше. При пробуксовке, они ещё посоревнуются с АМУРом, имеющим кэш, А уж на обработке прерываний, уделают его.
Не проверяли ли на контроллерах с кешированием всего кода - v203, v303, ...?
Не проверяли ли DMA?
На самом деле, задача была совсем другая. Я для студентов лекции с STM32G4 на другой контроллер перелицовываю. Причины описаны тут https://www.youtube.com/watch?v=Ny5z1lfVVm4 с продолжением тут https://www.youtube.com/watch?v=tVl5gbxIs5I
У меня в старых лекциях есть очень поучительная осциллограмма, где я перехожу с записи на чтение (потому что это мой любимый тест, я его на всех контроллерах делаю). Я просто хотел переснять её на этом контроллере. Но когда увидел всю эту красоту - не удержался и провёл исследования. И решил со всеми поделиться.
А на других контроллерах проверять мне некогда. Мне надо лекции перелицовывать на RISC-V и лабы переснимать. А праздники кончились - ещё и основная работа вернулась. Опыты велись в новогоднюю ночь. Статья писалась на выходных.
Рассматриваем циклы процессора в контроллере CH32x035