Получение набора инструментов GCC, позволяющего получить образ, подходящий для использования непосредственно на оборудовании без загрузки ОС, возможно, но включает в себя пару шагов, которые обычно не нужны для обычных программ.
Вам почти наверняка понадобится настроить модуль запуска времени выполнения C. Это модуль сборки (часто называемый чем-то вроде crt0.s
), который отвечает за инициализацию инициализированных данных, очистку BSS, вызов конструкторов для глобальных объектов, если включены модули C ++ с глобальными объектами, и т. Д. Типичные настройки включают необходимость настройки аппаратное обеспечение для фактического обращения к ОЗУ (возможно, включая настройку контроллера DRAM), чтобы было место для размещения данных и стека Некоторые процессоры должны выполнять эти действия в определенной последовательности: например, ColdFire MCF5307 имеет один выбор чипа, который отвечает на каждый адрес после загрузки, который в конечном итоге должен быть сконфигурирован так, чтобы охватывать только область карты памяти, запланированную для подключенного чипа.
Ваша аппаратная группа (или вы, возможно, с другой шляпой) должны иметь карту памяти, на которой записано, что находится по разным адресам. ПЗУ по адресу 0x00000000, ОЗУ по адресу 0x10000000, устройство регистрируется по адресу 0xD0000000 и т. Д. В некоторых процессорах команда аппаратного обеспечения могла подключить только микросхему выбора из ЦП к устройству и оставить на ваше усмотрение решение, какие триггеры адреса выбирают вывод .
GNU ld поддерживает очень гибкий язык сценариев компоновщика, который позволяет размещать различные разделы исполняемого образа в определенных адресных пространствах. Для обычного программирования вы никогда не увидите скрипт компоновщика, так как стандартный пакет поставляется gcc, который настроен на предположения вашей ОС для обычного приложения.
Вывод компоновщика в перемещаемом формате, который предназначен для загрузки в ОЗУ операционной системой. Он, вероятно, имеет исправления перемещения, которые необходимо завершить, и может даже динамически загружать некоторые библиотеки. В системе ПЗУ динамическая загрузка (обычно) не поддерживается, поэтому вы не будете этого делать. Но вам все еще нужно сырое двоичное изображение (часто в формате HEX, подходящем для программиста PROM какой-либо формы), поэтому вам нужно будет использовать утилиту objcopy из binutil для преобразования вывода компоновщика в подходящий формат.
Итак, чтобы ответить на вопрос, который вы задали ...
Вы используете скрипт компоновщика, чтобы указать целевые адреса каждого раздела изображения вашей программы. В этом сценарии у вас есть несколько вариантов работы с регистрами устройств, но все они включают в себя размещение сегментов текста, данных, bss-стека и кучи в диапазонах адресов, которые избегают аппаратных регистров. Существуют также механизмы, которые могут гарантировать, что ld выдает ошибку, если вы переполняете ПЗУ или ОЗУ, и вы должны также использовать их.
Фактически получение адресов устройств в ваш код на C можно сделать с помощью #define
, как в вашем примере, или путем объявления символа непосредственно в скрипте компоновщика, который разрешается в базовый адрес регистров, с соответствием extern
объявление в заголовочном файле C.
Хотя можно использовать атрибут GCC section
для определения экземпляра неинициализированного struct
как находящегося в определенном разделе (например, FPGA_REGS
), я обнаружил, что в реальных системах он не очень хорошо работает , Это может создать проблемы с обслуживанием, и это становится дорогим способом описания полной карты регистров встроенных устройств. Если вы используете эту технику, скрипт компоновщика будет отвечать за отображение FPGA_REGS
на его правильный адрес.
В любом случае вам потребуется хорошее понимание концепций объектных файлов, таких как «разделы» (в частности, разделы «текст», «данные» и «bss»), и вам, возможно, придется искать детали, которые соединяют разрыв между аппаратным и программным обеспечением, таким как таблица векторов прерываний, приоритеты прерываний, режимы супервизор и пользовательский режим (или звонки от 0 до 3 в вариантах x86) и т. п.