GPIO
Макроопределения базы и смещения
Возвращаясь к коду, давайте посмотрим на __metal_driver_sifive_gpio0_enable_output() снова:
int __metal_driver_sifive_gpio0_enable_output(struct metal_gpio *ggpio, long source) { long base = __metal_driver_sifive_gpio0_base(ggpio); __METAL_ACCESS_ONCE((__metal_io_u32 *)(base + METAL_SIFIVE_GPIO0_OUTPUT_EN)) |= source; return 0; }
Если вы будете следовать объявлению для base и METAL_SIFIVE_GPIO0_OUTPUT_EN, вы в конечном итоге найдете их макроопределения в ./bsp/install/include/metal/machine/platform.h:
/* From gpio@10012000 */ #define METAL_SIFIVE_GPIO0_10012000_BASE_ADDRESS 268509184UL #define METAL_SIFIVE_GPIO0_0_BASE_ADDRESS 268509184UL #define METAL_SIFIVE_GPIO0_10012000_SIZE 4096UL #define METAL_SIFIVE_GPIO0_0_SIZE 4096UL #define METAL_SIFIVE_GPIO0 #define METAL_SIFIVE_GPIO0_VALUE 0UL #define METAL_SIFIVE_GPIO0_INPUT_EN 4UL #define METAL_SIFIVE_GPIO0_OUTPUT_EN 8UL #define METAL_SIFIVE_GPIO0_PORT 12UL #define METAL_SIFIVE_GPIO0_PUE 16UL #define METAL_SIFIVE_GPIO0_DS 20UL #define METAL_SIFIVE_GPIO0_RISE_IE 24UL #define METAL_SIFIVE_GPIO0_RISE_IP 28UL #define METAL_SIFIVE_GPIO0_FALL_IE 32UL #define METAL_SIFIVE_GPIO0_FALL_IP 36UL #define METAL_SIFIVE_GPIO0_HIGH_IE 40UL #define METAL_SIFIVE_GPIO0_HIGH_IP 44UL #define METAL_SIFIVE_GPIO0_LOW_IE 48UL #define METAL_SIFIVE_GPIO0_LOW_IP 52UL #define METAL_SIFIVE_GPIO0_IOF_EN 56UL #define METAL_SIFIVE_GPIO0_IOF_SEL 60UL #define METAL_SIFIVE_GPIO0_OUT_XOR 64UL
В этом коде первым определением является значение, присвоенное base, а его длинное десятичное число равно 0x10012000. Вторая группа определений - это смещения регистра конфигурации. METAL_SIFIVE_GPIO0_OUTPUT_EN - четвертый.
Если вы вернетесь к таблицам 51 и 52 в руководстве FE310, вы увидите, что это действительно базовый адрес GPIO0 и смещение регистра output_en. Вот они, для вашего удобства:
![Базовый адрес GPIO0 и смещения регистра конфигурации (Взято с руководства пользователя FE310-G002, размещено с разрешения SiFive, Inc.)](/EDI/13_02_25_2/1739398847-5454/tutorial/1372/objects/4/files/03-10.jpg)
Рис. 3.10. Базовый адрес GPIO0 и смещения регистра конфигурации (Взято с руководства пользователя FE310-G002, размещено с разрешения SiFive, Inc.)
![Базовый адрес GPIO0 и смещения регистра конфигурации (Взято с руководства пользователя FE310-G002, размещено с разрешения SiFive, Inc.)](/EDI/13_02_25_2/1739398847-5454/tutorial/1372/objects/4/files/03-11.jpg)
Рис. 3.11. Базовый адрес GPIO0 и смещения регистра конфигурации (Взято с руководства пользователя FE310-G002, размещено с разрешения SiFive, Inc.)
Альтернативный способ увидеть, что происходит
Прежде чем двигаться дальше, важно еще раз подчеркнуть следующее: вам не обязательно глубоко копаться в библиотеке, если вы чувствуете себя в ней некомфортно. Вместо этого вы можете сидеть сложа руки и наблюдать.
Теперь мы увидим краткое описание процесса, который мы только что прошли. Это также более здравый взгляд на код, который на самом деле выполняется. Этот альтернативный процесс заключается в отладке приложения по мере его запуска на плате Red-V Thing Plus.
Как и раньше, мы хотим посмотреть, что происходит в строке 58 hello.c. Для этого давайте просто откроем проект Blinky и нажмем кнопку Debug.
Теперь, перед запуском приложения, поставьте точку останова непосредственно перед запуском строки 58. Чтобы сделать это, перейдите к строке 58 и дважды щелкните по желобу слева, прямо над цифрой 58.
Пошаговое выполнение
Для выполнения кода мы будем использовать кнопки выполнения кода:
![Resume, Suspend, Terminate, Disconnect, Step Into, Step Over, Step Return, Instruction Stepping Mode](/EDI/13_02_25_2/1739398847-5454/tutorial/1372/objects/4/files/03-14.jpg)
Рис. 3.14. Resume, Suspend, Terminate, Disconnect, Step Into, Step Over, Step Return, Instruction Stepping Mode
Если вы нажмете кнопку Resume , чтобы запустить приложение, оно остановится непосредственно перед выполнением строки 58.
Обратите внимание на следующие детали на рисунке выше:
Терминал показывает приветственное сообщение.
Выполнение находится в строке 58 в исходном коде C.
Разборка показывает реализацию вызова функции в виде 3 инструкций по сборке RISC-V.
Нажмите кнопку Step Into , чтобы перейти внутрь функции metal_gpio_enable_output(). После выполнения двух шагов вызов виртуальной функции можно найти в строке 98 из ./bsp/install/include/metal/gpio.h:
Один раз войдя в эту функцию, мы сразу попадаем в функцию __metal_driver_sifive_gpio0_enable_output():
Первый вызов функции - это фактическая функция, которая извлекает базовый адрес. Жаль, что приходится делать это во время выполнения, когда компилятор мог бы позаботиться об этом, сэкономив бесценное процессорное время.
После возврата из функции __metal_driver_sifive_gpio0_base() обратите внимание на состояние приложения непосредственно перед записью 1 в интересующий бит:
Напомним, что строка 61 в исходном коде C выполняет побитовую операцию OR с регистром output_en с маской в параметре source. Посмотрите на представление переменных слева: Значение source равно 0x20. Это 100000 в двоичном формате (маска с 1 в бите 5 и 0 в других), что именно то, что нам нужно, чтобы включить выходной буфер для вывода 5 в GPIO0.
Справа у нас есть множество инструкций по сборке, которые реализуют эту простую операцию. Опять же, использование функций для этого может оказаться затратным, поскольку переменные и аргументы должны быть извлечены. Этот заключительный шаг выполняется в 8 командах по сборке RISC-V. Вы сосчитали все команды до этого момента?
Теперь мы подтвердили, что output_en действительно высокоактивен, засвидетельствовав это во время выполнения.
Правда ли нам нужна библиотека Freedom Metal?
Вся структура библиотеки Freedom Metal, которую вы только что видели, может показаться немного избыточной для набора регистров, к которым вы можете получить доступ (в конце концов, output_en находится по адресу 0x10012008), но эта чрезвычайно модульная конструкция создана ради гибкости и переносимости.
Что еще более важно, весь процесс определения местоположения регистров и функций обычно выполняется компилятором один раз. Однако это не относится к библиотеке Freedom Metal, где большинство этих функций встроены и преобразуются компилятором в код на ассемблере.
С другой стороны, это не влечет за собой значительных накладных расходов (в конечном итоге все сводится к нескольким косвенным указаниям и вызовам функций), и конечное приложение в конечном итоге получит наиболее эффективный машинный код, который может создать компилятор.
Итак, суть в том, что вам, вероятно, лучше воспользоваться библиотекой Freedom Metal или любой другой официальной библиотекой, если уж на то пошло.
Тем не менее, давайте подумаем о создании вашего собственного кода на основе того, что мы узнали из руководства пользователя. Мы должны делать это ради максимальной эффективности и нулевой терпимости к накладным расходам.