Загрузка C99 "atomi c" в переносимую библиотеку baremetal - PullRequest
4 голосов
/ 16 июня 2020

Я работаю над переносимой библиотекой для встраиваемых приложений без оболочки.

Предположим, что у меня есть таймер ISR, который увеличивает счетчик, и в основном l oop считывание этого счетчика происходит при загрузке, безусловно, не atomi c.

Я пытаюсь обеспечить согласованность нагрузки (т.е. я не читаю мусор, потому что загрузка была прервана и значение изменилось), не прибегая к отключению прерываний. Не имеет значения, изменилось ли значение после считывания счетчика, если считанное значение является правильным. Это помогает?

uint32_t read(volatile uint32_t *var){
    uint32_t value;
    do { value = *var; } while(value != *var);
    return value;
}

Ответы [ 2 ]

2 голосов
/ 16 июня 2020

Крайне маловероятно, что для этого есть какое-либо портативное решение, не в последнюю очередь потому, что множество C -only платформ на самом деле C -только и используют одноразовые компиляторы, то есть ничего массового и современного-стандарта- податливый вроде g cc или лязг. Так что, если вы действительно нацеливаетесь на укоренившуюся C, тогда все это полностью зависит от платформы c и не переносимо - до такой степени, что поддержка «C99» становится безнадежной. Лучшее, что вы можете ожидать от переносимого кода C, - это поддержка ANSI C, относящаяся к самому первому не-черновому стандарту C, опубликованному ANSI. К сожалению, это до сих пор является общим знаменателем - крупные производители избегают неприятностей. Я имею в виду: Zilog каким-то образом сходит с рук, даже если сейчас они всего лишь подразделение Littelfuse, бывшего подразделения IXYS Semiconductor, которое приобрел Littelfuse.

Например, вот некоторые компиляторы, в которых есть только платформа. -specifi c способ сделать это:

  • Zilog eZ8 с использованием «недавнего» компилятора Zilog C (все, что старше 20 лет, в порядке): чтение 8-битного значения -modify-write - это atomi c. 16-битные операции, в которых компилятор генерирует словарные инструкции с выравниванием по словам, такие как LDWX, INCW, DECW, также являются atomi c. Если чтение-изменение-запись в противном случае умещается в 3 инструкции или меньше, вы должны добавить операцию с помощью asm("\tATM");. В противном случае вам нужно будет отключить прерывания: asm("\tPUSHF\n\tDI");, а затем снова включить их: asm("\tPOPF");.

  • Zilog ZNEO - это 16-битная платформа с 32-битными регистрами. , а доступ для чтения-изменения-записи к регистрам - это atomi c, но операции чтения-изменения-записи в памяти обычно выполняются через регистр и требуют 3 инструкции - таким образом, к операции RMW добавляется asm("\tATM").

  • Zilog Z80 и eZ80 требуют заключения кода в asm("\tDI") и asm("\tEI"), хотя это действительно только тогда, когда известно, что прерывания всегда разрешены при запуске вашего кода. Если они не могут быть включены, тогда возникает проблема, поскольку Z80 не позволяет считывать состояние IFF1 - триггер разрешения прерывания. Таким образом, вам нужно будет где-то сохранить «тень» его состояния и использовать это значение для условного включения прерываний. К сожалению, eZ80 не предоставляет регистр контроллера прерываний, который позволял бы получить доступ к IEF1 (eZ80 использует номенклатуру IEFn вместо IFFn), поэтому этот архитектурный упущение перенесено с почтенного Z80 на «современный». .

Это не обязательно самые популярные платформы, и многие люди не беспокоятся о компиляторах Zilog из-за их довольно низкого качества (достаточно низкого, чтобы вам действительно пришлось писать компилятор, ориентированный на eZ8 *). Тем не менее, такие странные углы являются основой кодовых баз, содержащих только C, и у библиотечного кода нет другого выбора, кроме как учесть это, если не напрямую, то, по крайней мере, путем предоставления макросов, которые можно переопределить с помощью специфичных для платформы c magi c.

Например, вы можете предоставить пустые по умолчанию макросы MYLIB_BEGIN_ATOMIC(vector) и MYLIB_END_ATOMIC(vector), которые будут использоваться для обертывания кода, требующего доступа atomi c по отношению к данному вектору прерывания (или, например, -1 если по всем векторам прерываний). Естественно, замените MYLIB_ на префикс «пространства имен» c в вашей библиотеке.

Чтобы включить оптимизацию c для конкретной платформы, например ATM vs DI на «современных» платформах Zilog макросу может быть предоставлен дополнительный аргумент для разделения предполагаемых «коротких» последовательностей, для которых компилятор склонен генерировать последовательности из трех инструкций по сравнению с более длинными. Такая микрооптимизация обычно требует аудита вывода сборки (легко автоматизируемого) для проверки предположения о длине последовательности команд, но, по крайней мере, данные для принятия решения будут доступны, и у пользователя будет выбор: использовать их или игнорировать. .


* Если какая-то заблудшая душа хочет знать что-нибудь, граничащее с тайной рекой. eZ8 - спроси. Я слишком много знаю об этой платформе, в деталях настолько кровавых, что даже современной голливудской компьютерной графике и спецэффектам было бы сложно воспроизвести истинную глубину происходящего на экране. Я также, возможно, единственный, кто запускает части eZ8 с частотой 20 МГц время от времени на частоте 48 МГц - это верный признак одержимости демонами, насколько позволяет мультивселенная. Если вы считаете возмутительным, что такая развратность воплощается в производственном оборудовании - я с вами. Увы, бизнес-кейс есть бизнес-кейс, к черту l aws физики.

1 голос
/ 16 июня 2020

Работаете ли вы в каких-либо системах, размер которых на uint32_t больше, чем размер чтения / записи одного слова инструкции сборки? Если нет, то ввод-вывод в память должен быть одной инструкцией и, следовательно, atomi c (при условии, что шина также имеет размер слова ...). У вас возникают проблемы, когда компилятор разбивает его на несколько более мелких операций чтения / записи. В противном случае мне всегда приходилось прибегать к DI / EI. Вы можете попросить пользователя сконфигурировать вашу библиотеку так, чтобы в ней была информация, доступны ли инструкции atomi c или минимальный 32-битный размер слова для предотвращения тиддлинга прерываний. Если у вас есть эти гарантии, вам не нужен код проверки.

Однако, чтобы ответить на вопрос, в системе, которая должна разделять чтение / запись, ваш код небезопасен. Представьте себе случай, когда вы правильно считываете свое значение в части «do», но значение разделяется во время проверки части «while». Далее, в крайнем случае, это бесконечное l oop. Для полной безопасности вам понадобится счетчик повторов и условие ошибки, чтобы предотвратить это. Случай l oop, конечно, крайний, но я бы хотел его на всякий случай. Это, конечно, увеличивает время выполнения.

Давайте на примерах покажем случай сбоя - будем использовать 16-битные числа на машине, которая считывает 8-битные значения за раз, чтобы упростить отслеживание:

  1. Значение для чтения из памяти * var равно 0x1234
  2. Чтение 8-битного 0x12
  3. * var становится 0x5678
  4. Чтение 8-битного 0x78 - значение теперь 0x1278 (недействительно)
  5. * var становится 0x1234
  6. На этапе проверки читается 8-битный 0x12
  7. * var становится 0x5678
  8. Проверка читает 8-битный 0x78

Значение подтверждено правильным 0x1278, но это ошибка, поскольку * var было только 0x1234 и 0x5678.

Другой случай сбоя будет, когда * var просто случайно изменится одновременно частота выполнения вашего кода, что может привести к бесконечному l oop, поскольку каждая проверка не удалась. Или даже если бы она в конце концов вырвалась, это было бы очень трудно отследить ошибку производительности.

...