Лучший способ добавить задержку/ничего не делать для n циклов процессора

Мне нужно добавить в мой код задержку в n циклов процессора (~ 30). Мое текущее решение приведено ниже, оно работает, но не очень элегантно.

Кроме того, задержка должна быть известна во время компиляции. Я могу работать с этим, но было бы идеально, если бы я мог изменить задержку во время выполнения. (Это нормально, если есть некоторые накладные расходы, но мне нужно разрешение в 1 цикл.)

У меня не осталось периферийных таймеров, которые я мог бы использовать, поэтому это должно быть программное решение.

do_something();
#define NUMBER_OF_NOPS   (SOME_DELAY + 3)
#include "nops.h"
#undef NUMBER_OF_NOPS
do_the_next_thing();

nops.h:

#if NUMBER_OF_NOPS > 0
    __ASM volatile ("nop");
#endif
#if NUMBER_OF_NOPS > 1
    __ASM volatile ("nop");
#endif
#if NUMBER_OF_NOPS > 2
    __ASM volatile ("nop");
#endif
...

person megle    schedule 20.06.2017    source источник
comment
У меня не осталось периферийных таймеров -- ну, вы можете мультиплексировать один таймер в программном обеспечении. Но этого, вероятно, недостаточно, если вам действительно нужны задержки с точностью до цикла....   -  person    schedule 20.06.2017
comment
Программные задержки ужасны, поэтому я предлагаю ужасное решение: 30 последовательных nop инструкций с вычисляемым переходом к ним.   -  person Weather Vane    schedule 20.06.2017
comment
Мне было бы интересно, почему вам нужны задержки с точностью до цикла? Может быть, есть какое-то лучшее решение, работающее с меньшей точностью? Если нет, предложение @WeatherVane кажется хорошей идеей.   -  person    schedule 20.06.2017
comment
Единственная причина, по которой у вас могут закончиться периферийные таймеры, заключается в том, что все они заблокированы аппаратными ресурсами, такими как ШИМ или захват ввода и т. д. Если это не так, значит, что-то очень неправильно в вашей программе. Но если вам нужны все таймеры для оборудования, то у вас, вероятно, есть отдельный RTC, который вы можете использовать для создания универсального драйвера таймера.   -  person Lundin    schedule 20.06.2017
comment
У меня не осталось периферийных таймеров, которые я мог бы использовать, поэтому это должно быть программное решение. - Тогда вам следует перепроектировать свою архитектуру. В семействах STM32 достаточно таймеров. Не уверен, но разве в CM0 нет таймера SysTick, как в CM3/4/7?   -  person too honest for this site    schedule 20.06.2017
comment
«Все в порядке, если есть некоторые накладные расходы, но мне нужно разрешение в 1 цикл» - значит, вы вообще не используете прерывания?   -  person ThingyWotsit    schedule 20.06.2017
comment
Кстати, маловероятно, что вам действительно нужна наносекундная точность на обычном STM32. Если вы это сделаете, вы выбрали совершенно неправильный процессор для задачи. Вместо этого вам, вероятно, потребуется использовать какой-нибудь специализированный DSP. Это похоже на типичную проблему XY.   -  person Lundin    schedule 20.06.2017
comment
Для точности ваше решение хорошее. Остается вопрос, зачем вам это нужно.   -  person Gerhard    schedule 20.06.2017
comment
@ThingyWotsit Код выполняется в ISR с наивысшим приоритетом.   -  person megle    schedule 20.06.2017
comment
@Olaf У него есть таймер SysTick, и мы его используем, но только с результатом 1 мс.   -  person megle    schedule 20.06.2017
comment
@Lundin Да, все таймеры используются для управления оборудованием.   -  person megle    schedule 20.06.2017
comment
@ Герхард Хорошо. Я подумал, что лучше всего обобщить проблему для вопроса, но, может быть, я действительно сосредоточился на этом одном из способов ее решения. Что я хочу сделать, так это установить некоторую задержку между отключением и включением функции прерывания TIM1 в STM32F051, чтобы добиться некоторого гашения. См. стр. 388 в Справочное руководство   -  person megle    schedule 20.06.2017
comment
@megle: Вы не можете запрограммировать SysTick для разрешения 1 мс! Это простой счетчик с CPU_CLK/1 или /8. Подумайте об этом!   -  person too honest for this site    schedule 20.06.2017
comment
Да, все таймеры используются для аппаратного управления. - IIRC есть по крайней мере один TIM (6 и/или 7 IIRC), который не имеет внешнего подключения. В любом случае, если вы забыли о таких задержках, похоже, что ваша системная архитектура немного испорчена. Что бы это ни было, циклы ЦП определенно являются очень плохим подходом к таким системам. Что, если произойдет прерывание?   -  person too honest for this site    schedule 20.06.2017
comment
@Olaf Я имел в виду, что прерывание для SysTick настроено на каждую мс. Я не уверен в тактовой частоте SysTick, я должен это проверить.   -  person megle    schedule 20.06.2017
comment
Как я и предполагал: доступны TIM6 и 7. Так почему бы не использовать их? Или прочитайте SysTick и сравните (если вы не используете делитель /1, измените это!). Но это все равно плохой подход.   -  person too honest for this site    schedule 20.06.2017


Ответы (3)


В устройствах коры NOP — это то, что буквально ничего не значит. Нет никакой гарантии, что NOP будет потреблять какое-то время. Они используются только для заполнения. Если у вас будет несколько последовательных NOP, они будут просто сброшены из конвейера.

Для получения дополнительной информации обратитесь к документации Cortex-M0. http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/CHDJJGFB.html

Программные задержки довольно сложны в устройствах Cortex, и вместо этого вам следует использовать другие инструкции +, возможно, барьерные инструкции.

используйте инструкции ISB 4 такта + время доступа к флэш-памяти, которое зависит от того, на какой скорости работает ядро. Для очень точных задержек поместите эту часть кода в SRAM.

person 0___________    schedule 20.06.2017
comment
Его нужно получить из флэш-памяти, это займет некоторое время, так что NOP вполне подойдет. M0 не имеет кеша. - person followed Monica to Codidact; 20.06.2017
comment
Но у него есть конвейер, и если вы выполняете трудоемкие инструкции, они будут просто извлечены и отброшены, поэтому, когда вы вводите процедуру задержки - это может быть 0 или совершенно непредсказуемое время - что делает их бесполезными. ISB - 4 цикла + доступ к флэш-памяти - person 0___________; 20.06.2017
comment
Это не займет меньше времени, чем время, необходимое для чтения такого количества байтов из флэш-памяти. И больше не потребуется, так как (как вы говорите) они будут выброшены из пайплайна. Они должны каким-то образом сначала войти в конвейер, а потом быть отброшенными, не так ли? - person followed Monica to Codidact; 20.06.2017
comment
@berendi Боюсь, вам следует больше узнать о ядре Cortex, прежде чем иметь такое твердое мнение. Лучший источник — сайт банкоматов. Даже ядро ​​M0 реализует многоступенчатый конвейер, и с точки зрения программиста вы не можете предсказать, сколько NOP было отброшено, если только вы не используете инструкцию ISB. Но если вы знаете лучше, чем ARM - конечно можете, но вместо этого я предпочитаю доверять официальной документации. Ядра Cortex не похожи на AVR или UC 51 года. - person 0___________; 21.06.2017
comment
В официальных документах указано, что это зависит от реализации, но я никогда не видел реализации, в которой NOP занимает нулевые циклы. Однако вы можете столкнуться с задержками предварительной выборки и другими сложностями, поэтому перед фактическим использованием любой NOP-процедуры следует измерять. - person Tony K; 21.06.2017
comment
Я до сих пор не понимаю, почему бы не использовать инструкции с предсказуемыми таймингами. Встроенная процедура NOP зависит от кода, который был выполнен ранее. - person 0___________; 21.06.2017
comment
В этом случае вы можете переместить регистр в себя, например a-timer" title="как задержать кору руки m0 на n циклов без таймера"> stackoverflow.com/questions/27510198/ - person Tony K; 21.06.2017
comment
Между прочим, ISB имеет разное количество циклов на разных ядрах, например, на M0+ это 3 цикла из-за более короткого конвейера. - person Tony K; 21.06.2017
comment
Конечно, они зависят от реализации, но такие подпрограммы задержки с точностью до тика тоже. - person 0___________; 21.06.2017
comment
Таким образом, время выполнения ISB не более предсказуемо, чем время выполнения NOP. - person followed Monica to Codidact; 21.06.2017
comment
Это когда вы выполняете его на конкретном микро, а не на виртуальном. Всегда критические части должны быть написаны для фактического ядра. - person 0___________; 21.06.2017

Изменить: есть лучший ответ от другого SO Q&A a-timer">здесь. Однако в сборке AFAIK с использованием счетчика, такого как SysTick, является единственным способом гарантировать какое-либо подобие точности цикла.

Редактировать 2: Чтобы избежать переполнения счетчика, что приведет к очень и очень большой задержке, очистите счетчик SysTick перед использованием, т.е. SysTick->VAL = 0;

Оригинал:

Cortex-M имеют встроенный таймер под названием SysTick, который можно использовать для точной синхронизации циклов.

Сначала включите таймер:

SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
               SysTick_CTRL_ENABLE_Msk;

Затем вы можете прочитать текущий счет, используя регистр VAL. Затем вы можете реализовать точную задержку цикла следующим образом:

int count = SysTick->VAL;
while(SysTick->VAL < (count+30));

Обратите внимание, что это приведет к некоторым накладным расходам из-за загрузки, сравнения и ветвления в цикле, поэтому итоговое количество циклов будет немного отличаться, по моей оценке, не более чем на несколько тиков.

person Tony K    schedule 21.06.2017
comment
Исходный вопрос касается задержек с точностью до цикла ЦП, SysTick здесь бесполезен. Обработка прерывания уже близка к требуемой задержке :) - person Code Painters; 21.06.2017
comment
Код не требует прерывания, это просто цикл, считывающий счет снова и снова. - person Tony K; 21.06.2017
comment
Да, я знаю - извините за неясность, я дал IRQ только в качестве ссылки. Тем не менее моя точка зрения остается в силе, я верю. - person Code Painters; 21.06.2017
comment
IMO, это лучшее, что вы можете получить на чистом C, поскольку в противном случае вы не можете гарантировать время цикла любой данной процедуры. Даже подсчет инструкций может быть сопряжен с опасностью (конвейер, состояния ожидания памяти, неопределенность кеша и т. д.), как показывают другие ответы и комментарии. - person Tony K; 21.06.2017
comment
Как я уже говорил в своем ответе, для очень точных результатов такой критический блок должен быть помещен в память SRAM. На более новых ядрах — в CCMRAM или ICCMRAM - person 0___________; 21.06.2017
comment
Остерегайтесь переполнения счетчика. Приведенный выше код зависнет навсегда, если его вызвать в нужное время. - person followed Monica to Codidact; 21.06.2017
comment
@berendi спасибо за уловку, я добавил инструкцию по очистке счетчика перед использованием, что должно избежать переполнения во всех задержках, кроме самых длинных (2 ^ 23 цикла) - person Tony K; 22.06.2017

Вы можете использовать бесплатный ап-счетчик следующим образом:

uint32_t t = <periph>.count;
while ((<periph>.count - t) < delay);

Пока delay меньше половины периода счетчика, на него не влияет перенос значения счетчика - беззнаковая арифметика дает правильную временную дельту.

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

person Jeremy    schedule 08.07.2017
comment
Я думаю, вы не поняли проблемы. Доступ, счетчики и сравнение занимают больше времени, чем необходимая задержка, и это не точное время - person 0___________; 08.07.2017