Подсчет циклов на современных процессорах (например, ARM)

14

Во многих приложениях ЦП, выполнение команд которого имеет известную временную зависимость с ожидаемыми входными стимулами, может обрабатывать задачи, для которых потребуется гораздо более быстрый ЦП, если бы эта связь была неизвестна. Например, в проекте, в котором я использовал PSOC для генерации видео, я использовал код для вывода одного байта видеоданных каждые 16 тактовых частот процессора. Поскольку проверка того, готово ли устройство SPI, и разветвление, если нет, IIRC потребует 13 часов, а загрузка и сохранение для вывода данных - 11, невозможно было проверить устройство на готовность между байтами; вместо этого я просто устроил так, чтобы процессор выполнял ровно 16 циклов кода для каждого байта после первого (я думаю, что я использовал реальную индексированную нагрузку, фиктивную индексированную загрузку и хранилище). Первая запись SPI каждой строки произошла до начала видео, и для каждой последующей записи было окно с 16 циклами, в котором запись могла происходить без переполнения или переполнения буфера. Ветвящийся цикл генерировал окно с 13 циклами неопределенности, но предсказуемое выполнение с 16 циклами означало, что неопределенность для всех последующих байтов будет соответствовать тому же окну с 13 циклами (которое в свою очередь вписывается в окно с 16 циклами, когда запись могла быть приемлемой происходит).

Для более старых процессоров информация о сроках выполнения команд была четкой, доступной и однозначной. Для более новых ARM информация о времени кажется гораздо более расплывчатой. Я понимаю, что когда код выполняется из флэш-памяти, поведение при кэшировании может усложнить прогнозирование, поэтому я ожидаю, что любой код с циклическим счетом должен выполняться из ОЗУ. Однако даже при выполнении кода из ОЗУ спецификации кажутся немного расплывчатыми. Является ли использование кода с циклическим счетом все еще хорошей идеей? Если да, то каковы лучшие методы, чтобы заставить его работать надежно? В какой степени можно смело предположить, что производитель чипа не собирается молча вставлять «новый улучшенный» чип, который в определенных случаях сокращает цикл выполнения определенных инструкций?

Предполагая, что следующий цикл начинается на границе слова, как определить, исходя из спецификаций, сколько именно времени потребуется (предположим, Cortex-M3 с памятью с нулевым состоянием ожидания; в этом примере больше ничего не должно иметь значения для системы).

myloop:
  mov r0, r0; Короткие простые инструкции, позволяющие получать дополнительные инструкции
  mov r0, r0; Короткие простые инструкции, позволяющие получать дополнительные инструкции
  mov r0, r0; Короткие простые инструкции, позволяющие получать дополнительные инструкции
  mov r0, r0; Короткие простые инструкции, позволяющие получать дополнительные инструкции
  mov r0, r0; Короткие простые инструкции, позволяющие получать дополнительные инструкции
  mov r0, r0; Короткие простые инструкции, позволяющие получать дополнительные инструкции
  добавляет r2, r1, # 0x12000000; Инструкция из двух слов
  ; Повторите следующее, возможно, с другими операндами
  ; Будет продолжать добавлять значения, пока не произойдет перенос
  ITCC
  addcc r2, r2, # 0x12000000; Инструкция из двух слов, плюс дополнительное слово для itcc
  ITCC
  addcc r2, r2, # 0x12000000; Инструкция из двух слов, плюс дополнительное слово для itcc
  ITCC
  addcc r2, r2, # 0x12000000; Инструкция из двух слов, плюс дополнительное слово для itcc
  ITCC
  addcc r2, r2, # 0x12000000; Инструкция из двух слов, плюс дополнительное слово для itcc
; ... и т.д., с более условными инструкциями из двух слов
  sub r8, r8, # 1
  bpl myloop

Во время выполнения первых шести инструкций ядро ​​успевает извлечь шесть слов, из которых будет выполнено три, так что может быть до трех предварительно выбранных. Следующие инструкции состоят из трех слов, поэтому ядро ​​не сможет извлечь инструкции так быстро, как они выполняются. Я ожидал бы, что некоторые из "it" -процессов будут занимать цикл, но я не знаю, как предсказать, какие из них.

Было бы хорошо, если бы ARM мог указать определенные условия, при которых синхронизация команд «it» была бы детерминированной (например, если нет состояний ожидания или конфликта кодовой шины, а предыдущие две инструкции являются 16-битными командами регистра и т. Д.) но я не видел такой спецификации.

Образец заявки

Предположим, что кто-то пытается спроектировать дочернюю плату для Atari 2600, чтобы генерировать компонентный видеовыход с разрешением 480P. 2600 имеет тактовую частоту 3,597 МГц и тактовую частоту процессора 1,19 МГц (точка часов / 3). Для компонентного видео 480P каждая строка должна выводиться дважды, что подразумевает точечный выход 7.158 МГц. Поскольку видеочип Atari (TIA) выводит один из 128 цветов, используя в качестве 3-битного сигнала яркости плюс фазовый сигнал с разрешением примерно 18 нс, было бы трудно точно определить цвет, просто взглянув на выходы. Лучшим подходом было бы перехватывать записи в регистры цвета, наблюдать записанные значения и подавать в каждый регистр значения яркости TIA, соответствующие номеру регистра.

Все это может быть сделано с помощью FPGA, но некоторые довольно быстрые устройства ARM могут быть намного дешевле, чем FPGA с достаточным объемом оперативной памяти для обработки необходимой буферизации (да, я знаю, что для томов такая вещь может быть произведена, цена не реальный фактор). Однако требование ARM следить за входящим тактовым сигналом значительно увеличит требуемую скорость процессора. Предсказуемое количество циклов может сделать вещи чище.

Относительно простой подход к проектированию заключается в том, чтобы CPLD следил за процессором и TIA и генерировал 13-битный сигнал синхронизации RGB +, а затем имел бы ARM DMA, получая 16-битные значения из одного порта и записывая их в другой с надлежащей синхронизацией. Однако было бы интересно узнать, может ли дешевый ARM сделать все. DMA может быть полезным аспектом подхода «все в одном», если его влияние на количество циклов ЦП можно предсказать (особенно если циклы DMA могут происходить в циклах, когда шина памяти в противном случае не используется), но в какой-то момент процесса ARM должен будет выполнять свои функции поиска по таблице и наблюдения за шиной. Обратите внимание, что в отличие от многих видео архитектур, в которых регистры цвета записываются во время интервалов гашения, Atari 2600 часто записывает в регистры цвета во время отображаемой части кадра,

Возможно, наилучшим подходом было бы использовать пару дискретных логических чипов для идентификации записи цвета и принудительно установить младшие биты регистров цвета в надлежащие значения, а затем использовать два канала DMA для выборки входной шины ЦП и выходных данных TIA, и третий канал DMA для генерации выходных данных. В этом случае центральный процессор сможет свободно обрабатывать все данные из обоих источников для каждой строки сканирования, выполнять необходимый перевод и буферизовать его для вывода. Единственным аспектом обязанностей адаптера, который должен был бы выполняться в «реальном времени», было бы переопределение данных, записанных в COLUxx, и об этом можно было бы позаботиться, используя два распространенных логических чипа.

Supercat
источник

Ответы:

7

Я голосую за DMA. Это действительно гибко в Cortex-M3 и выше - и вы можете делать все сумасшедшие вещи, такие как автоматическое получение данных из одного места и вывод в другое с заданной скоростью или в некоторых случаях, не тратя ЛЮБЫХ циклов ЦП. DMA намного надежнее.

Но это может быть довольно сложно понять в деталях.

Другой вариант - программные ядра на ПЛИС с аппаратной реализацией этих трудностей.

BarsMonster
источник
1
Мне нравится понятие DMA. Я не думаю, что ядро ​​Cortex M3 имеет DMA, хотя - это функция чипов отдельных производителей, и все они, похоже, реализуют это по-разному. Одна вещь, которую я нахожу утомительной по крайней мере с одной реализацией, с которой я на самом деле играл (STM32L152), это то, что я не могу найти какой-либо способ иметь пин-строб при выводе данных DMA. Также не ясно, какие факторы могут повлиять на своевременность DMA.
суперкат
1
В любом случае, что касается одного из первых приложений, которые я размышлял о точном циклическом ударе, я разместил больше информации в исходном вопросе. Мне интересно, что вы думаете. Другой ситуацией, когда я размышлял о циклическом ударе, было бы отображение данных на цветном ЖК-дисплее. Данные будут буферизироваться в ОЗУ с использованием 8-битных цветов, но для отображения требуются 16-битные цвета. Самый быстрый способ, которым я думал о выводе данных, - это использовать аппаратное обеспечение для генерации стробов записи, поэтому ЦП должен был только синхронизировать данные. Было бы хорошо перевести 8-> 16 бит в небольшой буфер ...
суперкат
1
... а затем организовать DMA для передачи этого, или что будет лучшим подходом?
суперкат
4

Информация о времени доступна, но, как вы указали, иногда может быть расплывчатой. В Разделе 18.2 и Таблице 18.1 Технического справочного руководства для Cortex-M3 содержится много временной информации , например ( pdf здесь ), и выдержка здесь:

выдержка из 18,2

которые дают список условий для максимального времени. Время для многих инструкций зависит от внешних факторов, некоторые из которых оставляют неясности. Я выдвинул на первый план каждую неясность, которую я нашел в следующем отрывке из этого раздела:

[1] Ветви берут один цикл для инструкции, а затем перезагружают конвейер для целевой команды. Неиспользованные ветви - всего 1 цикл. Взятые ответвления, как правило, представляют собой 1 цикл перезагрузки трубопровода (всего 2 цикла). Взятые ветви с операндом регистра - это обычно 2 цикла перезагрузки конвейера (всего 3 цикла). Перезагрузка конвейера дольше [Насколько дольше?] При переходе к невыровненным 32-разрядным инструкциям в дополнение к доступу к более медленной памяти. Подсказка ветвления передается на шину кода, которая позволяет более медленной системе [насколько медленнее?] Выполнить предварительную загрузку. Это может [это необязательно?] Уменьшить [На сколько?] Штраф за целевую ветвь для более медленной памяти, но не меньше, чем показано здесь.

[2] Как правило, инструкции по сохранению нагрузки занимают два цикла для первого доступа и один цикл для каждого дополнительного доступа. Магазины с немедленным смещением занимают один цикл.

[3] UMULL / SMULL / UMLAL / SMLAL используют досрочное завершение в зависимости от размера исходных значений [Какие размеры?]. Они прерываются (прекращаются / перезапускаются), с наихудшей задержкой в ​​один цикл. Версии MLAL занимают от четырех до семи циклов, а версии MULL занимают от трех до пяти циклов . Для MLAL подписанная версия на один цикл длиннее неподписанной.

[4] IT-инструкции можно сложить . [Когда? Смотрите комментарии.]

[5] Время DIV зависит от дивиденда и делителя . [Та же проблема, что и в MUL] DIV прерывается (прекращается / перезапускается) с наихудшей задержкой в ​​один цикл. Когда дивиденды и делители имеют одинаковый размер [насколько похожи?], Деление быстро завершается. Минимальное время для делителей больше, чем делитель и делитель нуля. Делитель нуля возвращает ноль (не ошибка), хотя для отлова этого случая доступна отладочная ловушка. [Какие диапазоны были даны для MUL?]

[6] Сон - это один цикл для инструкции плюс столько циклов сна, сколько необходимо. WFE использует только один цикл, когда событие прошло. WFI, как правило, более одного цикла, если только прерывание не происходит точно при входе в WFI.

[7] ISB занимает один цикл (действует как ветвь). DMB и DSB занимают один цикл, если только данные не ожидают в буфере записи или LSU. Если прерывание появляется во время барьера, оно отменяется / перезапускается.

Для всех случаев использования он будет более сложным, чем «Эта инструкция - один цикл, эта инструкция - два цикла, это один цикл ...», подсчет возможен в более простых, медленных и старых процессорах. В некоторых случаях вы не столкнетесь с неясностями. Если вы столкнулись с неясностями, я предлагаю:

  1. Обратитесь к вашему поставщику и спросите его, каково время инструкций для вашего варианта использования.
  2. Тест для определения неоднозначного поведения
  3. Повторно протестируйте любые ревизии процессора, особенно, когда вы меняете поставщика.

Эти требования, вероятно, дают ответ на ваш вопрос: «Нет, это не очень хорошая идея, если возникшие трудности не стоят затрат», - но вы уже это знали.

Кевин Вермеер
источник
1
Я бы посчитал следующее неопределенным: «конвейерная перезагрузка длиннее при переходе к невыровненным 32-битным инструкциям в дополнение к доступу к более медленной памяти», не сказано, добавляет ли он ровно один цикл, а «IT-инструкции можно сложить» не не уточняйте при каких условиях они будут или не будут.
суперкат
1
Время «IT» может показаться особенно тревожным, так как это инструкция, которая часто используется в тесном цикле с циклическим счетом, и я уверен, что его не всегда можно сложить. Я полагаю, что если всегда переходить к началу чувствительного к времени цикла, заставляет цикл начинаться с границы слова, избегает любых условных нагрузок или запоминающих элементов в цикле, и не сразу помещает «IT» инструкцию после загрузки или сохранения в регистре обновления «IT» будут согласованы, но в спецификации это не ясно.
Суперкат
1
Я полагаю, что ИТ-специалист, вероятно, мог бы (правдиво) заметить что-то вроде: «В отсутствие состояний ожидания или конфликта шины кода, ИТ-свертывание гарантировано, если (1) предыдущая инструкция была 16-битной инструкцией, которая не обращалась к память или счетчик программ, а также (2) либо следующая инструкция является 16-битной инструкцией, либо предыдущая инструкция не была целью "невыровненной" ветви. Свертывание ИТ также может происходить в других неуказанных обстоятельствах ". Такая спецификация позволила бы писать программы с предсказуемой синхронизацией IT-инструкций, гарантируя, что код был расположен так, как указано.
Суперкат
1
Ничего себе - я признаюсь, что я прошел только простые подсчеты циклов наихудшего случая, а не боролся с предостережениями под столом. Мой обновленный ответ подчеркивает некоторые другие неясности.
Кевин Вермеер
1
Существует множество ситуаций, когда кто-то интересуется подсчетами в худшем случае, и справедливое число, когда он интересуется подсчетами в наилучшем случае (например, если порт SPI может выводить один байт каждые 16 циклов, генерация каждого байта займет 14 циклов). в лучшем случае, и проверка готовности займет 5 циклов, проверка готовности каждого байта будет ограничивать скорость до одного байта каждые 19 циклов в лучшем случае, а запись вслепую с двумя добавленными NOP позволит скорость одного байта каждые 16 циклов в лучшем случае ). Случаи, когда требуется точное время, не так распространены, но могут возникнуть.
суперкат
3

Одним из способов обойти эту проблему является использование устройств с детерминированными или предсказуемыми временными интервалами, таких как чипы Parallax Propeller и XMOS:

http://www.parallaxsemiconductor.com/multicoreconcept

http://www.xmos.com/

Подсчет циклов очень хорошо работает с Propeller (необходимо использовать язык ассемблера), в то время как устройства XMOS имеют очень мощную программную утилиту, XMOS Timing Analyzer, которая работает с приложениями, написанными на языке программирования XC:

https://www.xmos.com/download/public/XMOS-Timing-Analyzer-Whitepaper%281%29.pdf

Леон Хеллер
источник
1
Я начинаю думать, что Леон имеет доли в XMOS ... ;-)
Федерико Руссо
1
Мне просто нравятся их фишки и люди, которые там работают. Параллакс - хорошая компания с хорошими продуктами.
Леон Хеллер
1
Да, без обид. Меня просто поражает, что все ответы (кроме одного), где упоминается XMOS, принадлежат вам. Нет ничего плохого в том, чтобы быть в восторге от чего-то.
Федерико Руссо
@Federico, @Leon - Это именно то, что меня немного беспокоит в XMOS: почему в мире всего 1 пользователь (по крайней мере, так это выглядит)? Если это так здорово, то почему не говорят о городе? Я никогда не слышал, чтобы кто-то говорил об этом, меньше используй это.
Stevenvh
Попробуйте форумы XMOS: xcore.com
Леон Хеллер
2

Подсчет циклов становится более проблематичным, когда вы переходите от низкоуровневых микроконтроллеров к более универсальным вычислительным процессорам. Первые обычно имеют четко определенные сроки инструкций, отчасти по причинам, которые вы посещаете. Это также потому, что их архитектура довольно проста, поэтому время инструкций фиксировано и понятно.

Хорошим примером этого являются большинство микрочип PIC. Серии 10, 12, 16 и 18 имеют очень хорошо документированные и предсказуемые сроки выполнения команд. Это может быть полезной функцией для небольших приложений управления, для которых предназначены эти чипы.

Поскольку вы избавляетесь от сверхнизких затрат, и поэтому дизайнер может потратить немного больше места на чипах, чтобы получить более высокую скорость от более экзотической архитектуры, вы также избавитесь от предсказуемости. Взгляните на современные варианты x86 как крайние примеры этого. Существует несколько уровней кэшей, витализация памяти, выборочная проверка, конвейерная обработка и многое другое, что делает подсчет циклов команд практически невозможным. В этом приложении это не имеет значения, так как клиент заинтересован в высокой скорости, а не в предсказуемости синхронизации команд.

Вы даже можете увидеть этот эффект на более высоких моделях микрочипов. 24-битное ядро ​​(серии 24, 30 и 33) имеет в значительной степени предсказуемую синхронизацию команд, за исключением нескольких исключений, когда существуют конфликты шины регистра. Например, в некоторых случаях машина вставляет остановку, когда следующая инструкция использует регистр с некоторыми режимами косвенной адресации, значение которого было изменено в предыдущей инструкции. Этот тип сбоев необычен для dsPIC, и большую часть времени вы можете его игнорировать, но он показывает, как эти вещи закрадываются из-за дизайнеров, пытающихся предоставить вам более быстрый и более мощный процессор.

Таким образом, основной ответ - это часть компромисса, когда вы выбрали процессор. Для небольших приложений управления вы можете выбрать что-то маленькое, дешевое, с низким энергопотреблением и с предсказуемым временем выполнения команды. По мере того, как вам требуется больше вычислительной мощности, архитектура меняется, так что вам приходится отказываться от предсказуемой синхронизации команд. К счастью, это не проблема, так как вы переходите к приложениям, требующим больших вычислительных ресурсов и общего назначения, поэтому я думаю, что компромиссы работают достаточно хорошо.

Олин Латроп
источник
Я согласен с тем, что в целом приложения, которые требуют больших вычислительных ресурсов, становятся менее чувствительными к микроскопическим временным характеристикам, но есть некоторые сценарии, в которых может потребоваться немного больше вычислительной мощности, чем в PIC-18, но также требуется предсказуемость. Мне интересно, в какой степени я должен стремиться изучать такие вещи, как 16-разрядные архитектуры PIC, или в какой степени я должен полагать, что ARM, вероятно, будет адекватным.
суперкат
0

Да, вы все еще можете сделать это, даже на ARM. Самая большая проблема с этим в ARM состоит в том, что ARM продает ядра, а не чипы, и тактирование ядра известно, но то, что оборачивает поставщик чипов, варьируется от поставщика к поставщику, а иногда от семейства чипов к другому в пределах поставщика. Таким образом, конкретный чип от конкретного поставщика может быть весьма детерминированным (например, если вы не используете кэши), но портировать его становится сложнее. При работе с 5 часами здесь и 11 часами там использование таймеров является проблематичным, так как количество инструкций, необходимых для выборки таймера и определения, истекло ли время ожидания. Судя по звукам вашего прошлого опыта программирования, я готов поспорить, что вы, вероятно, отлаживаете осциллограф так же, как и я, поэтому вы можете попробовать плотную петлю на чипе с тактовой частотой, посмотреть на spi или i2c или любую форму сигнала, добавить или удалить nops, изменить количество раз через цикл и в основном настроить. Как и с любой платформой, отсутствие использования прерываний значительно помогает детерминированной природе выполнения команд.

Нет, это не так просто, как PIC, но все же вполне выполнимо, особенно если задержка / время приближаются к тактовой частоте процессора. Ряд поставщиков на основе ARM позволяют вам умножить тактовую частоту и получить, скажем, 60 МГц от эталона 8 МГц, поэтому, если вам нужен интерфейс с частотой 2 МГц вместо выполнения каких-либо действий через каждые 4 инструкции, вы можете повысить тактовую частоту (если у вас есть энергопотребление), а затем используйте таймер и дайте себе много часов, чтобы заниматься другими делами.

Старожил
источник