Контроль прочитанного и записи ширины доступа к регистраторам памяти в C

StackOverflow https://stackoverflow.com/questions/3040000

Вопрос

Я использую и Core на основе X86, чтобы манипулировать 32-битным памяти памяти. Мое оборудование ведет себя правильно, только если ЦП генерирует 32-битные ширины и пишет в этот реестр. Регистр выровнен на 32-битном адресе и не является адресатом в расширении байта.

Что я могу сделать, чтобы гарантировать, что мой компилятор C (или C99) будет генерировать только полные 32-битные ширины и пишет во всех случаях?

Например, если я сделаю операцию для чтения-модификации, как это:

volatile uint32_t* p_reg = 0xCAFE0000;
*p_reg |= 0x01;

Я не хочу, чтобы компилятор был умным о том, что только нижние байтовые изменения изменяются и генерируют 8-битные широководные / пишеты. Поскольку машинный код часто является более плотным для 8-битных операций на X86, я боюсь ненужных оптимизаций. Отключение оптимизаций в целом не является вариантом.

----- РЕДАКТИРОВАТЬ -------
Интересный и очень актуальный документ: http://www.cs.utah.edu/~Regehr/Papers/emsoft08-preprint.pdf.

Это было полезно?

Решение

Ваши проблемы покрыты volatile Квалификатор.

6.7.3 / 6 "Тип Квалификаторы" говорит:

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

5.1.2.3 «Исполнение программы» говорит (среди прочего):

На абстрактной машине все выражения оцениваются как указанные семантикой.

За этим следует предложение, которое обычно называют правилом «As - if», которое позволяет осуществлять не следовать абстрактной семантике машины, если конечный результат одинаково:

Фактическая реализация не должна оценивать часть выражения, если она может вывести, что его значение не используется, и что не требуются необходимые побочные эффекты (включая любую вызванную, вызывая функцию или доступа к летучему объекту).

Но, в основном, 6,7,3 / 6, по существу, говорит, что волатильные квалифицированные типы, используемые в выражении, не могут иметь применение правила «As - if» - необходимо соблюдать фактическую семантику абстрактной машины. Следовательно, если указатель на летучий 32-битный тип разветвляется, то полное 32-битное значение должно быть прочитано или записано (в зависимости от операции).

Другие советы

Единственный способ гарантировать, что компилятор сделает правильные вещи, - это написать свою нагрузку и хранить процедуры в ассемблере и позвонить им из C. 100% компиляторов, которые я использовал в течение многих лет, и получит это неправильно (GCC включен) Отказ

Иногда оптимизатор позволяет вам, например, вы хотите сохранить некоторую константу, который появляется под компилятору как небольшое количество 0x10, позволяет сказать, в 32-битный регистр, который является то, что вы спрашивали специально и то, что я смотрел, иначе хорошие компиляторы пытаются сделать Отказ Некоторые компиляторы решит, что дешевле сделать 8 бит, а не 32 бит написать и изменить инструкцию. Цели длины переменной инструкции состоит в том, чтобы сделать это хуже, так как компилятор пытается сохранить пространство программы, а не только циклы памяти на то, что он может взять на себя. (XOR AX, AX вместо MOV EAX, 0 Например)

И с чем-то, что постоянно развивается, как GCC, код, который сегодня работает, не имеет гарантий работы завтра (вы не можете даже компилировать некоторые версии GCC с текущей версией GCC). Аналогичным образом код, который работает на компиляторе на вашем столе, может не работать универсально для других.

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

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

Ну, вообще говоря, я не ожидаю, что он оптимизирует высокий байт заказа, если у вас есть реестр, набранный как 32-битный волатильный. Благодаря использованию волатильного ключевого слова Компилятор не может предположить, что значения в байтах высокого порядка являются 0x00. Таким образом, он должен написать полные 32биты, даже если вы используете только 8-битное буквальное значение. Я никогда не испытываю проблему с этим на процессорах 0x86 или Ti или других встроенных процессоров. Обычно достаточно волатильное ключевое слово. Единственным временем становится немного странным, является если процессор не в сопоставлении не поддерживает размер слова, который вы пытаетесь написать, но это не должно быть проблемой на 0x86 для 32 битного номера.

Хотя компилятор мог бы получить компилятор для создания потока инструкций, который использовал 4 бит, который не был бы оптимизацией в любом времени процессора или пространству обучения в течение одного 32 бит.

Если вы не используете типы Byte (unsigned Char) при доступе к оборудованию, будет лучший шанс компилятора, не генерируя 8-битные инструкции по передаче данных.

volatile uint32_t* p_reg = 0xCAFE0000;
const uint32_t value = 0x01;  // This trick tells the compiler the constant is 32 bits.
*p_reg |= value;

Вам придется прочитать порт в качестве 32-битного значения, измените значение, а затем запишите назад:

uint32_t reg_value = *p_reg;
reg_value |= 0x01;
*p_reg = reg_value;

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

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

Я бы порекомендовал, поскольку Dwelch предлагает написать свою собственную функцию записи-модификации-записи в сборке, если это имеет решающее значение.

Я никогда не слышал о компиляторе, который оптимизирует тип (выполнение преобразования типа с целью оптимизации). Если он объявлен как int32, это всегда int32 и всегда будет выровненным вправо в памяти. Проверьте свою документацию компилятора, чтобы увидеть, как работают различные оптимизации.

Я думаю, что знаю, откуда исходит ваша забота, сооружения. Структуры обычно дополняются к оптимальному выравниванию. Вот почему вам нужно подключить пакет #pragma () вокруг них, чтобы получить их байт, выровненный.

Вы можете просто выйти через сборку, а затем вы увидите, как компилятор перевел свой код. Я уверен, что он не изменил ваш тип.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top