1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/QQxiaoming-quard_star_tutorial

Клонировать/Скачать
DEVELOPNOTE.md 68 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 30.11.2024 13:00 de1d109

Авторские заметки (разработка дневника)

  • 2021.07.04(после полудня): Добавлена плата Quard Star с поддержкой 8 ядер, добавлены контроллер прерываний, последовательный порт и pflash. Код для низкоуровневого запуска зашит в pflash.

  • 2021.07.05(вечер): Добавлен исходный код opensbi и добавлена поддержка платформы, код для низкоуровневой загрузки добавляет функцию загрузки sbi.

  • 2021.07.09(вечер): В opensbi добавлено определение домена в устройстве, а также добавлен код безопасности домена. Используется последовательный порт 3 для печати; добавлен код u-boot и загружено в небезопасный домен для выполнения, используется последовательный порт 0 для печати.

  • 2021.07.10(полдень): Добавлены определения платформы u-boot, а также код конфигурации и т. д.

  • 2021.07.10(после полудня): Попытка загрузить корневую файловую систему и образ ядра, созданные ранее, успешно. Обнаружены некоторые проблемы с файлом дерева устройств:

    1. При обновлении файла устройства opensbi до версии с исправлением ошибок добавляется недоступный домен в зарезервированный узел памяти устройства, что может привести к конфликту при анализе устройства в u-boot. В этом случае домен был изменён на разделённый домен, хотя это не является элегантным решением, но оно временно решает проблему.
    2. На этапе запуска bl1 u-boot копирует код в область высокого адреса DDR. Если эта область настроена как недоступная область безопасного домена, текущая реализация riscv в u-boot не проверяет, находится ли она в области зарезервированной памяти. Чтобы решить эту проблему, мы добавили хук-функцию в наш код платформы board для изменения gd->ram_top. Это простой и эффективный способ, который не требует анализа всего дерева устройств. Однако это лишь небольшой этап процесса запуска. Тем не менее, похоже, что u-boot всё больше склоняется к анализу дерева устройств.
    3. Было обнаружено, что linux5.10.42 не работает должным образом при использовании qemu с драйвером virtio-gpu-device. Ядро выдаёт ошибку, и пока неясно, является ли это проблемой qemu-6.0.0 или ошибкой в коде ядра. Я планирую изучить этот вопрос позже.
  • 2021.07.11(утро): Компиляция ядра Linux, упрощённая компиляция busybox, написание сценария упаковки образа корневой файловой системы, добавление упрощённого файла конфигурации etc, и наконец, запуск оболочки успешно. Далее необходимо начать улучшать конфигурацию системы, пользовательские программы и добавлять новые графические драйверы. Мы планируем перенести Qt-интерфейс в систему. Кроме того, мы планируем внедрить RTOS, такие как FreeRTOS или RTThread, в безопасный домен (⊙o⊙)?

  • 2021.07.12(вечер): При добавлении виртуального дисплея virtio с помощью опции -device virtio-gpu-device возникает ошибка, связанная с переполнением адреса. После некоторого времени поиска я обнаружил, что проблема заключается в строке 242 файла virtgpu_kms.c в ядре Linux-5.10.42/drivers/gpu/drm/virtio. Здесь vgdev является пустым указателем, и прямой доступ к нему вызывает ошибку. Почему он пустой? Потому что на строке 110 проверка VIRTIO_F_VERSION_1 не проходит, поэтому драйвер не загружается нормально, и при освобождении возникает пустой указатель. Теперь, когда мы знаем причину проблемы, нам нужно найти её в исходном коде qemu. После поиска мы обнаружили, что в файле qemu-6.0.0/hw/virtio/virtio-mmio.c только если proxy->legacy установлен в false, можно включить эту функцию, а по умолчанию установлено значение true. Поэтому мы добавили опцию -global virtio-mmio.force-legacy=false при запуске qemu, и теперь всё работает нормально. Здесь мы можем обратиться к этой статье о разработке qemu для получения дополнительной информации. Из неё видно, что force-legacy, установленный в false, соответствует обновлённой версии, но возможно, из соображений совместимости, qemu по умолчанию использует true для обеспечения традиционного поведения virtio. Решив эту проблему, мы повторно добавили параметры ядра и определили файл конфигурации quard_star_defconfigs для нашего ядра Linux. Затем мы снова запустили qemu и всё заработало! Ниже приведены фрагменты кода из текста:

    ……
    110 if (!virtio_has_feature(dev_to_virtio(dev->dev), VIRTIO_F_VERSION_1))
    111     return -ENODEV;
    ……
    242 virtio_gpu_modeset_fini(vgdev);
    ……
    ……
    712 if (!proxy->legacy) {
    713     virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1);
    714 }
    ……
    722 DEFINE_PROP_BOOL("force-legacy", VirtIOMMIOProxy, legacy, true),
    ……
  • 2021.07.13(вечер): Мы заменили оболочку, используемую для запуска, с busybox sh на стандартную bash, чтобы упростить дальнейшую разработку. Однако самой сложной задачей было установить библиотеку ncurses. Динамическую библиотеку можно скопировать из кросс-компилятора, но некоторые файлы конфигурации и данные, связанные с терминалом, должны быть установлены после компиляции библиотеки ncurses. К счастью, основная часть работы завершена. Обратите внимание, что начиная с этого момента, все изменения, внесённые в загрузочный образ roofs.img, будут выполняться путём монтирования общего каталога виртуальной машины и установки файлов, а не путём создания нового образа с использованием сценариев. Поэтому важно избегать перестроения образа, если только он не повреждён, и использовать виртуальную машину для внесения изменений в файлы внутри образа.

2021.07.14(вечер): RTC и syscon были добавлены в систему, решая проблему неточного времени в виртуальной машине и проблему невозможности сброса системы через reboot. Ранее поле resetvec в ядре не было определено для базового адреса rom, а использовало значение по умолчанию 0x1000 (можно увидеть в поле "resetvec" в файле cpu.c qemu-6.0.0). Хотя это не вызывало ошибок, при запуске процессора происходила попытка чтения с адреса 0x1000, что не соответствовало нашим ожиданиям. После исправления этой проблемы мы заметили ошибку. Для наблюдения за поведением можно добавить опцию запуска qemu -d in_asm -D qemu_run_asm.log. Также стоит отметить, что реализация перезагрузки и выключения питания в текущем ядре не совсем элегантна. Поскольку sbi уже поддерживает выключение питания, попытка регистрации syscon-poweroff будет неудачной, но syscon-reboot будет успешной, поскольку sbi ещё не реализовал перезагрузку. Учитывая, что opensbi всё ещё находится в стадии активной разработки, ожидается, что эти проблемы будут постепенно решаться. Заинтересованные стороны могут следить за развитием этих проектов. Однако мы не будем углубляться в детали этого дизайна.

2021.07.15(вечер): sudo и screenfetch были перенесены в систему. Были внесены небольшие изменения в screenfetch, так как мы используем busybox вместо стандартной оболочки. Также благодаря @zhiliao007 (wenqing.li@aliyun.com) мы узнали, какие пакеты необходимо установить для запуска проекта. Поскольку мой компьютер уже имеет множество инструментов разработки, я не мог определить, какие базовые библиотеки необходимы для новой Ubuntu-машины. Я выражаю свою благодарность за помощь.
  • 2021.07.17(полдень): Последние несколько дней были довольно напряжёнными, но наконец-то у меня появилось время продолжить работу над проектом. Во-первых, мы использовали опцию --display gtk,zoom-to-fit=false для запуска qemu с графическим интерфейсом gtk. Затем я продолжил переносить некоторые полезные инструменты, такие как tree,screen и cu. Особенно важен screen, так как работа с командной строкой может продолжаться ещё долго, и наличие многофункционального многотерминального инструмента становится критически важным. Перенос этих инструментов относительно прост, но передача динамических размеров окон терминала от хоста к цели может быть сложной задачей. Здесь fw_cfg, который мы использовали ранее, играет ключевую роль. Мы вычисляем размеры окон терминала с хоста и передаём их в целевую систему через fw_cfg. Целевая система может затем прочитать эти значения через cat /sys/firmware/qemu_fw_cfg/by_name/opt/qemu_cmdline/raw. Стоит отметить, что функция fw_cfg была добавлена в версию qemu 6.0 для архитектуры riscv, а в текущей версии ядра Linux 5.10.42 она ещё не реализована. Однако мы можем легко настроить ядро для использования этой функции. Можно ожидать, что последние версии ядра уже включают эту функциональность, и те, кто хочет попробовать новое ядро, могут обновить его для новых впечатлений. Вот изображение, демонстрирующее текущий результат разработки.

    img0

  • 2021.07.18(полдень): Вчера вечером мы начали подготовку к переносу FreeRTOS в доверенный домен. Однако после загрузки последней версии FreeRTOS мы обнаружили, что официальные ресурсы доступны только для riscv. Перевод текста на русский язык:

Пакет переноса в режиме S основан на режиме M, так что давайте сами этим займёмся. В конце концов, FreeRTOS — это самая знакомая мне операционная система. После вечера и большей части сегодняшнего дня перенос наконец-то удался. Он состоит из трёх основных частей:

  • использование системного вызова sbi_set_timer для таймера Tick;
  • планирование контекста задач в режиме S, не нужно работать с регистром CSR;
  • использование sbi_send_ipi для отправки мягкого прерывания через portYIELD самому ядру.

На данный момент основные функции перенесены успешно. Известно, что есть ещё две детали, которые необходимо оптимизировать:

  1. Использовался стек прерываний, но он не поддерживает вложение. Скретч не используется и неэффективен. Нужно проанализировать, можно ли поддерживать вложенность прерываний.
  2. Не сохраняется и не восстанавливается кадр стека с плавающей точкой. При следующем добавлении нужно тщательно продумать этот вопрос. Лучше всего реализовать ленивое нажатие на стек.

Однако текущий перенос уже удовлетворяет большинству требований. Я не знаю, был ли это первый в мире перенос режима S FreeRTOS, но я не нашёл никого, кто бы его опубликовал.

В режиме S в реальном времени могут быть проблемы, но пока тестирование в qemu проходит хорошо. Конечно, мне нужно провести дальнейшее тестирование, а затем добавить некоторые тесты системы реального времени и IPC-связь Linux-системы, чтобы посмотреть.

24 июля (утро):

Не было большого прогресса на этой неделе. На этот раз я использовал https://github.com/riscv/riscv-gnu-toolchain tag: 2021.08.07 (зеркало gitee: https://gitee.com/mirrors/riscv-gnu-toolchain). Из репозитория были скомпилированы компилятор для Linux и компилятор для разработки на голом железе. Это имеет три преимущества:

  • разработка на голом железе может использовать c-библиотеку newlib;
  • не нужно полагаться на инструменты, предоставленные другими людьми, что удобно при отладке и отслеживании исходного кода;
  • мы скомпилировали gdb-инструмент, который можно использовать для анализа проблем как на голом железе, так и в приложениях Linux.

После обновления компилятора я обновил некоторые сценарии компиляции при повторной компиляции target_root_app. Большинство проблем было решено, но экран работает неправильно. Я провёл грубый анализ, но не смог найти проблему. Позже я займусь её поиском.

26 июля (вечер):

Я продолжал отлаживать проблемы с экраном, и сегодня вечером они наконец были решены. Здесь обязательно нужно записать сложный путь (>﹏<)!

Сначала я запустил screen и получил сообщение «[screen is terminating]», после чего сразу же вышел. Это было очень странно. Моя первая мысль заключалась в том, чтобы открыть Makefile-файл экрана и включить «OPTIONS = -DDEBUG», а затем снова запустить его. Появится информация журнала в /tmp/debug/{SCREEN, screen}.*, которую можно сравнить с предыдущей нормальной информацией журнала. Ничего необычного обнаружено не было. Затем я попытался перекомпилировать strace, но никаких аномалий не обнаружил. У меня не было другого выбора, кроме как перекомпилировать gdb (перекомпиляция gdb вызовет много ошибок, в основном из-за проблем с путями в официальном скрипте компиляции, которые я исправил вручную, и, наконец, успешно скомпилировал его). Я скопировал полученный инструмент gdb на целевую плату и использовал его для отладки экрана. Снова открыв Makefile экрана, я изменил «CFLAGS = -g -O0 -D_GNU_SOURCE», чтобы облегчить отладку с помощью gdb. Вскоре я обнаружил screen.c:1235, где вызывается fork, и основной процесс не генерирует исключение после этого, но получает сигнал SIGHUP и завершается. Таким образом, проблема должна быть в дочернем процессе.

Я вспомнил, что, возможно, strace не проверял журнал дочернего процесса, поэтому я повторно использовал «strace -f /bin/screen» для проверки, но всё равно не обнаружил ничего необычного. Странно! Мне пришлось продолжить использовать gdb для отслеживания дочернего процесса. Метод заключается в том, чтобы войти в gdb, а затем использовать команду «set follow-fork-mode child», чтобы указать gdb отслеживать дочерний процесс. Наконец, экран был расположен в screen.c:1428, и MakeWindow (&nwin) вернул -1, вызвав ошибку. Дальнейшее отслеживание показало, что ошибка была вызвана следующим вызовом:

screen.c:1428:MakeWindow(&nwin)
>>>>window.c:628:OpenDevice(nwin.args, nwin.lflag, &type, &TtyName)
>>>>>>>>window.c:1132:OpenPTY(namep)
>>>>>>>>>>>>pty.c:334:openpty(&f, &s, TtyName, NULL, NULL)

Проблема, похоже, связана с openpty, которая определена в glibc login/openpty.c:86. Используя gdb для дальнейшего поиска, мы обнаружили, что openpty.c:99 master = getpt() возвращает -1. Определение getpt имеет три версии: login/getpt.c, sysdeps/unix/bsd/getpt.c и sysdeps/unix/sysv/linux/getpt.c. Я догадался, что проблема в том, что мы используем собственный компилятор, а bootlin использует другой компилятор. Я могу только догадываться, что bootlin использует sysdeps/unix/bsd/getpt.c, тогда как мы используем sysdeps/unix/sysv/linux/getpt.c. Разница в том, что pty-устройства linux используют /dev/ptmx для создания узлов устройств в каталоге /dev/pts, тогда как устройства BSD используют /dev/pty*.

Затем я написал простой тестовый код для открытия устройства /dev/ptmx и обнаружил, что он действительно возвращает -1. Затем мне пришлось проверить конфигурацию ядра, чтобы убедиться, что оно поддерживает оба типа pty. Как оказалось, это так. Мы создали файл конфигурации целевой файловой системы /etc/fstab с использованием mdev для генерации каталога /dev, но мы не создали каталог /dev/pts. Поэтому мы добавили его в сценарий запуска файла /etc/init.d/rcS в файле /sbin/mdev -s:

mkdir /dev/pts
mount -t devpts devpts /dev/pts

OK, после тестирования экрана он работает отлично.

27 июля (вечер):

Мы добавили опцию запуска full-screen, надеясь, что она сможет запускать полный экран и адаптироваться к разрешению хоста. Однако мы обнаружили проблему с графическим интерфейсом qemu GTK. Перед запуском графического драйвера разрешение было низким. После завершения настройки разрешение стало правильным, но GTK не обновлял окно после изменения разрешения. В итоге весь полноэкранный режим не работал должным образом. Наконец, мы нашли проблему в qemu-6.0.0/ui/gtk.c:601. Функция gd_update_windowsize обновляет размер только в обычном режиме. Мы внесли следующие изменения:

if(vc->s->full_screen){
    gtk_menu_item_activate(GTK_MENU_ITEM(vc->s->full_screen_item));
    gtk_menu_item_activate(GTK_MENU_ITEM(vc->s->full_screen_item));
} else {
    gd_update_windowsize(vc);
}

Таким образом, размер будет обновляться правильно в полноэкранном режиме.

28 июля (вечер):

Программа trusted_domain на голой плате теперь может использовать newlib. Поэтому я изменил сценарий запуска и выполнил соответствующий перенос функций newlib stub. Но компиляция не удалась. Причина в том, что наш компилятор использует -mcmodel=medlow для компиляции библиотеки c, а мы используем -mcmodel=medany для обеспечения связи с адресами выше 0x80000000. Нам пришлось перекомпилировать. Окончательная команда компиляции выглядит следующим образом:

# 用于裸机带newlib的gcc
./configure --prefix=/opt/gcc-riscv64-unknown-elf --with-cmodel=medany
make -j16
# 用于linux带glibc的gcc
./configure --prefix=/opt/gcc-riscv64-unknown-linux-gnu
make linux -j16

Теперь у нас есть полностью обновлённый компилятор. Если мы столкнёмся с проблемами в будущем, нам не придётся беспокоиться о проблемах с инструментами разработки, предоставленными другими. У нас есть все исходные коды, которые мы можем скомпилировать, отладить и исправить самостоятельно. O(∩_∩)O Ха-ха~

29 июля (вечер):

Мы успешно выполнили кросс-компиляцию qt-5.12.11. Ранее мы пробовали версию qt-5.15.2, но обнаружили, что эта версия имеет проблему, из-за которой она не может нормально скомпилироваться без включения opengl. Перевод текста на русский язык:

В bugreports](https://bugreports.qt.io/browse/QTBUG-88017) уже описана эта проблема, поэтому здесь мы используем более стабильную версию LTS 5.12.11.

30 июля (вечер): в ядро добавлена опция CONFIG_INPUT_EVDEV для удобства использования qt-приложений /dev/input/event* для получения входных событий с мыши и клавиатуры. Было скомпилировано несколько тестовых примеров qt gui-приложений, всё работает нормально. Для обеспечения нормальной работы qt-приложения необходимо добавить некоторые переменные окружения. Qt-библиотека ещё не полностью развёрнута в коде, я хочу протестировать больше функций, прежде чем рассматривать детали развёртывания, чтобы сделать процесс развёртывания и отладки более элегантным.

export QT_QPA_FB_DRM=1
export QT_QPA_GENERIC_PLUGINS=evdevkeyboard
export QT_QPA_GENERIC_PLUGINS=evdevmouse
export QT_QPA_EVDEV_MOUSE_PARAMETERS=/dev/input/event0
export QT_QPA_EVDEV_KEYBOARD_PARAMETERS=/dev/input/event1
export QT_PLUGIN_PATH=/home/root/plugins

Кроме того, в начале года на основе Qt была перенесена эмуляция симулятора World of Gaea и FC, проект также был развёрнут в текущей среде, компиляция прошла успешно. Пробный запуск выглядит следующим образом:

img1

img2

Ха-ха-ха, это так смешно! Успешно запущен симулятор qemu на платформе riscv, работающей под Linux, на x86 PC, и перенесённое приложение m68k на основе fb qt framework, запущенное на симуляторе World of Gaea MD, слой за слоем, попробуйте поиграть, частота кадров неописуема, ха-ха-ха (смеюсь и плачу)!

1 августа (после полудня): добавлен сетевой конфигурационный скрипт, модифицирован стартовый скрипт для настройки сети, dhcp и других функций, добавлены сетевые инструменты ethtool и iperf. Всё прошло гладко, обратите внимание на синтаксис в /etc/inittab, который является busybox, а не sysvinit, не путайте.

2 августа (вечер): добавлены серии стартовых скриптов, содержание которых взято из buildroot. Добавлены zlib и openssh исходные коды, добавлены скрипты компиляции и развёрнуты, qemu использует переадресацию портов 22 на физический порт 3522, ssh и sftp полностью доступны для тестирования. На этой неделе начинается завершение некоторых сетевых задач, хотя я всё ещё новичок в этой области, но мне повезло, что у меня не было никаких проблем, и есть много учебных материалов, которые я собираюсь начать изучать более глубоко.

6 августа (вечер): некоторые инструменты в target_root_app могут не компилироваться напрямую в оригинальной версии Ubuntu 18.04, чтобы обеспечить успешное прохождение, на данный момент выбран automake версии 1.16.1 и pkg-config версии 0.29.2, мы непосредственно импортируем исходный код в host_output, а затем используем host_output automake для выполнения процесса компиляции.

14 августа (после полудня): продолжаю пополнять блог. Сегодня в Шанхае проливной дождь, и я закончил три обновлённых до ch15 за один раз. Увы, сегодня праздник Цисицзе, почему я пишу блог дома? Конечно, потому что сегодня я не договорился о встрече с девушкой (горько)! Это обновление достаточно большое, и после этого я отдыхаю и смотрю сериал.

18 августа (вечер): попытка кросс-компиляции openjdk11, слишком много зависимостей, особенно в наборе библиотек x11, но она была успешно завершена, и на этот раз я полностью понял логику реализации automake и pkg-config. openjdk zero действительно имеет низкую производительность, выполнение одной команды для просмотра версии занимает семь или восемь минут. Позже я планирую изучить реализацию jit, хотя я пока не думаю, что смогу перенести её на rv, но это всё равно очень интересно.

21 августа (утро): продолжаем ускорять редактирование серии блогов, завершив три раздела за один раз, сейчас мы находимся в ch19, и у нас есть план закончить ch20-ch25 эти шесть разделов в ближайшее время, надеюсь завершить обновление в течение следующих нескольких дней, вперёд!

21 августа (вечер): не ожидал, что утром я закончу редактирование документов, но днём и вечером я завершил все обновления, и теперь этот проект действительно вступает в период замедления обновлений. В будущем мне нужно будет изучить и дополнить технологии, которые мне ещё не знакомы.

16 октября (после полудня): я вернулся к обновлению этого проекта. Прежде всего, я добавил три контроллера i2c в наш виртуальный SOC. Здесь мы выбираем уже существующую реализацию imx-i2c, которая является IP-адресом i2c на NXP imx series. Это довольно просто, и он был успешно добавлен. Затем мы повесили at24c64 eeprom на i2c0 и написали файл устройства дерева устройств. В ядре Linux мы добавили соответствующий драйвер. Очень легко распознать контроллер i2c и at24c64. Вы можете использовать следующие команды для проверки чтения и записи eeprom.

echo hello > /sys/bus/i2c/devices/0-0050/eeprom
cat /sys/bus/i2c/devices/0-0050/eeprom

17 октября (утро): продолжить добавление двух контроллеров spi в наш виртуальный soc. Здесь мы выбрали уже существующую реализацию sifive_spi. После этого мы повесили is25wp256 norflash на spi0 и написали файл дерева устройств. Мы добавили соответствующий драйвер в ядро Linux и не забыли добавить драйвер mtd. Затем мы успешно увидели устройство /dev/mtd1 (mtd0 — это pflash, подключённый напрямую к параллельному порту нашего soc). Тестирование mtd-устройства не требует дополнительных объяснений, вы можете напрямую управлять mtdblock1 или использовать небольшой инструмент mtd_debug для управления. Наши внутренние контроллеры soc становятся всё более разнообразными, поэтому нам нужно организовать код qemu-6.0.0/hw/riscv/quard_star.c. Ранее он был в основном упрощён из virt.c, но поскольку фокус отличается, его читаемость не очень хорошая для нас. Здесь я реконструировал его, написав отдельную функцию create для каждого отдельного контроллера. Теперь код стал более аккуратным.

static void quard_star_machine_init(MachineState *machine)
{
    quard_star_cpu_create(machine);
    quard_star_interrupt_controller_create(machine);
    quard_star_memory_create(machine);
    quard_star_flash_create(machine);
    quard_star_syscon_create(machine);
    quard_star_rtc_create(machine);
    quard_star_serial_create(machine);
    quard_star_i2c_create(machine);
    quard_star_spi_create(machine);
}

30 октября (утро): в последнее время моё настроение стало намного стабильнее, и я чувствую себя очень счастливым. Продолжайте обновлять этот проект. Прежде всего, это файл дерева устройств. Раньше я добавлял контент для наглядности. Это очень полезно для начинающих, но по мере того, как количество информации об устройствах становится всё больше и больше, линейный метод написания становится трудночитаемым, и его необходимо реорганизовать в структурированный формат. Здесь я повторно написал следующие три файла: quard_star_sbi.dts, quard_star_uboot.dts и quard_star.dtsi. dtsi — это общая информация об устройстве внутри soc, а два других dts ссылаются на dtsi и включают соответствующие контроллеры в зависимости от их потребностей. Внешние устройства, подвешенные к контроллеру, также записываются в dts, а не в dtsi. Кроме того, этот репозиторий был импортирован в CodeFactor, и некоторые стилистические проблемы были оптимизированы в соответствии с рекомендациями. В дальнейшем мы должны не только заставить код работать правильно, но и улучшить стиль кода и читаемость.

6 ноября (утро): Прежде всего, оптимизирован загрузочный скрипт uboot distro. Теперь мы поддерживаем tftpboot. При отладке в будущем нам не нужно каждый раз создавать файловую систему с информацией о ядре, а нужно только скомпилировать загрузочное изображение и загрузить его через tftp. Содержание скрипта заключается в том, чтобы загрузить основной файл загрузки через tftp и проверить, является ли содержимое «tftp». Если нет этого файла или содержимое не является tftp, то выполняется предыдущий процесс запуска. ``` else echo virtioboot... load virtio 0:1 ${kernel_addr_r} /Image load virtio 0:1 ${fdt_addr_r} /quard_star.dtb fi

# boot kernel
booti ${kernel_addr_r} - ${fdt_addr_r}

Это похоже на язык **QEMU** или **VirtIO**.
2021.11.06(下午):应该是这周最后一次更新,说起来今天还有点小忧伤,这篇更新完毕晚上准备给自己做顿盐焗鸡翅。不扯废话了,这次我们给虚拟SOC添加一个USB控制器,qemu中有dwc3的半成品实现,为啥是半成品呢——因为只实现了host模式,而没有实现otg。dwc3这个控制器想必做嵌入式的朋友都太熟悉了,Synopsys的ip,我在非常多的SOC中都见过这个控制器。阅读代码看到这个dwc3仿真似乎是Xilinx写的,被包含在xlnx-usb-subsystem中一部分,这里我们不使用XILINX_VERSAL的代码,直接创建一个dwc3设备。代码还是很简单的,如下:

```c
static void quard_star_usbs_create(MachineState *machine)
{
    QuardStarState *s = RISCV_VIRT_MACHINE(machine);

    object_initialize_child(OBJECT(s), "dwc3", &s->usb,
                            TYPE_USB_DWC3);

    sysbus_realize(SYS_BUS_DEVICE(&s->usb), &error_fatal);

    sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb), 0, 
                            virt_memmap[QUARD_STAR_USB].base);
    qdev_pass_gpios(DEVICE(&s->usb.sysbus_xhci), DEVICE(&s->usb), SYSBUS_DEVICE_GPIO_IRQ);
    sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb), 0,
                    qdev_get_gpio_in(DEVICE(s->plic), QUARD_STAR_USB_IRQ));
}
```

编写设备树文件,然后配置内核CONFIG_USB_DWC3=y,运行,这里居然出现了oops,看起来是访问异常,难道说部分寄存器没实现导致读写出错了吗?翻看了代码qemu-6.0.0/hw/usb/hcd-dwc3.c:587这边模拟器是register_read_memory/register_write_memory来访问寄存器的,qemu和内核两头加打印,最终定位到是内核linux-5.10.65/drivers/usb/dwc3/core.c:290这里访问DWC3_DCTL出错,其实问题很明确,qemu注释写明了只支持host因此device相关的寄存器都是没有实现的,那么内核里的驱动为什么还访问设备寄存器呢,毕竟我们在设备树里指定了dr_mode为host,最终修改283行判断代码添加另一个条件检查dr_mode来跳过device寄存器。考虑Xilinx自己的驱动代码是5.13才加入内核主线的,因此查了下最新的内核5.15的驱动代码有所变化,但这个判断条件仍没有修改,是否修复了看不出来,这里就先暂且放下,先按我这个修改使用。

```c
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST ||
    dwc->dr_mode == USB_DR_MODE_HOST)
return 0;
```

搞定之后进入系统一切顺利,使用lsusb可以看到控制器信息。

```
[~]#lsusb 
Bus 001 Device 001: ID 1d6b:0002
Bus 002 Device 001: ID 1d6b:0003
```

Этот текст похож на техническую документацию по разработке и тестированию программного обеспечения. В нём описывается процесс добавления USB-контроллера в виртуальную систему на базе QEMU и проблемы, с которыми автор столкнулся при реализации этой задачи.

В тексте используются термины и понятия из области разработки программного обеспечения и аппаратных средств, такие как:

  • SOC (System on a Chip) — система на кристалле;
  • QEMU — эмулятор, который позволяет запускать операционные системы и приложения на одной платформе на другой;
  • VirtIO — набор драйверов и библиотек для виртуализации ввода-вывода;
  • USB (Universal Serial Bus) — универсальная последовательная шина;
  • GPIO (General Purpose Input/Output) — интерфейс ввода-вывода общего назначения;
  • IRQ (Interrupt Request) — запрос на прерывание;
  • DCTL (Device Control) — регистр управления устройством;
  • DR (Driver Role) — роль драйвера;
  • OTG (On-The-Go) — режим работы USB-устройства, когда оно может выступать как хост и как устройство одновременно.

Текст содержит фрагменты кода на языке C, которые описывают реализацию USB-контроллера и его взаимодействие с системой. Автор также упоминает о проблемах, связанных с доступом к регистрам устройства и их реализацией в симуляторе и ядре Linux. Здесь приведён перевод текста на русский язык:

«Если phys_pc равен -1, то размер блока TB равен 1. В действительности здесь было -1. Наш адрес pc в фазе maskrom начинается с нулевого адреса. Продолжаем отслеживать код до qemu-6.0.0/accel/tcg/cputlb.c:1518. Функция get_page_addr_code_hostp содержит следующий фрагмент кода:

if (unlikely(!tlb_hit(entry->addr_code, addr))) {
    if (!VICTIM_TLB_HIT(addr_code, addr)) {
        tlb_fill(env_cpu(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0);
        index = tlb_index(env, mmu_idx, addr);
        entry = tlb_entry(env, mmu_idx, addr);

        if (unlikely(entry->addr_code & TLB_INVALID_MASK)) {
            /*
             * Защита MMU охватывает меньший диапазон, чем целевая страница, поэтому мы должны повторять проверку MMU для каждой инструкции.
             */
            return -1;
        }
    }
    assert(tlb_hit(entry->addr_code, addr));
}

Здесь условие entry->addr_code & TLB_INVALID_MASK истинно. Почему 0-й адрес в состоянии включения питания имеет маску TLB_INVALID_MASK, а другие адреса не имеют этой проблемы? Продолжаем отслеживание и обнаруживаем функцию tlb_set_page_with_attrs, которая содержит следующий фрагмент:

if (size < TARGET_PAGE_SIZE) {
    /* Повторяем проверку MMU и заполнение TLB при каждом доступе.  */
    address |= TLB_INVALID_MASK;
}

Отслеживая источник исключения size, мы видим, что он исходит из tlb_set_page. Для платформы RISC-V вызов происходит в функции riscv_cpu_tlb_fill в файле qemu-6.0.0/target/riscv/cpu_helper.c через get_physical_address_pmp, который получает tlb_size и передаётся в tlb_set_page. Здесь мы переходим к специфическому коду платформы RISC-V. Продолжая отслеживать код, мы обнаруживаем, что в файле qemu-6.0.0/target/riscv/pmp.c функция pmp_is_range_in_tlb-->pmp_get_tlb_size возвращает значение 1 для размера tlb_sa в первом 4K регионе.

static target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
                                    target_ulong tlb_sa, target_ulong tlb_ea)
{
    target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa;
    target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea;

    if (pmp_sa >= tlb_sa && pmp_ea <= tlb_ea) {
        //printf("1 %ld %ld %ld %ld\n",pmp_sa,tlb_sa,pmp_ea,tlb_ea);
        return pmp_ea - pmp_sa + 1;
    }

    if (pmp_sa >= tlb_sa && pmp_sa <= tlb_ea && pmp_ea >= tlb_ea) {
        return tlb_ea - pmp_sa + 1;
    }

    if (pmp_ea <= tlb_ea && pmp_ea >= tlb_sa && pmp_sa <= tlb_sa) {
        return pmp_ea - tlb_sa + 1;
    }

    return 0;
}

Проблема заключается в том, что env->pmp_state.addr[pmp_index].sa и env->pmp_state.addr[pmp_index].ea равны 0. Во время выполнения кода pmp_state не был настроен должным образом, sa и ea имеют значения по умолчанию после выделения памяти. Изучая реализацию pmp_update_rule_addr, можно увидеть, что состояние PMP_AMATCH_OFF должно быть установлено для sa равным 0 и для ea равным максимальному значению. Это предотвратило бы возврат странного значения 1 функцией pmp_is_range_in_tlb. Очевидно, это серьёзная проблема, поскольку гостевая система не инициализирует pmp должным образом, что приводит к ошибкам в получении tlb_size.

void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index)
{
    uint8_t this_cfg = env->pmp_state.pmp[pmp_index].cfg_reg;
    target_ulong this_addr = env->pmp_state.pmp[pmp_index].addr_reg;
    target_ulong prev_addr = 0u;
    target_ulong sa = 0u;
    target_ulong ea = 0u;

    if (pmp_index >= 1u) {
        prev_addr = env->pmp_state.pmp[pmp_index - 1].addr_reg;
    }

    switch (pmp_get_a_field(this_cfg)) {
    case PMP_AMATCH_OFF:
        sa = 0u;
        ea = -1;
        break;

    case PMP_AMATCH_TOR:
        sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */
        ea = (this_addr << 2) - 1u;
        break;

    case PMP_AMATCH_NA4:
        sa = this_addr << 2; /* shift up from [xx:0] to [xx+2:2] */
        ea = (sa + 4u) - 1u;
        break;

    case PMP_AMATCH_NAPOT:
        pmp_decode_napot(this_addr, &sa, &ea);
        break;

    default:
        sa = 0u;
        ea = 0u;
        break;
    }

    env->pmp_state.addr[pmp_index].sa = sa;
    env->pmp_state.addr[pmp_index].ea = ea;
}

Поскольку логика этого кода довольно сложна, я пока не нашёл подходящего места для добавления инициализации начальных значений pmp_state.addr. Поэтому моим решением на данный момент является добавление следующих строк в начале maskrom:

csrw pmpcfg0, 0
csrw pmpcfg1, 0
csrw pmpcfg2, 0
csrw pmpcfg3, 0

Это устанавливает правильную конфигурацию pmp и решает проблему. Наконец, TCG QEMU очень интересен. Я рекомендую несколько блогов для всех, кто интересуется этой темой». 2021.12.04 (в полночь): Сейчас функционал проекта почти готов, идей особо нет, осталось только немного доработать мелочи.

Затем в эти два дня пришла внезапная идея сделать красивый графический интерфейс для этого проекта в виде панели разработки, на которой можно было бы нажать переключатель запуска qemu, а также нажать на последовательный порт и VGA-интерфейс, чтобы увидеть эмуляцию вывода, нажать на flash-устройство для загрузки прошивки и т. д. Хотя техническая ценность не так велика, но это очень весело и интересно. После одного дня усилий этот инструмент наконец-то обрёл форму. Я записал гифку, чтобы все могли посмотреть.

![img7](./tutorial/img/img7.gif)

2021.12.05 (вечером): Доработка графического интерфейса инструментов, добавление параметров командной строки, обновление основного экрана, который выглядит не очень красиво. Здесь я особенно благодарен MignonZhu за предоставление 3D-модели, которая делает интерфейс более привлекательным. Кроме того, я теперь довольно хорошо знаком с GIMP, он полностью заменяет Photoshop, даже лучше.

2021.12.18 (после полудня): В последнее время на работе немного занят, и у меня нет времени заниматься этим. Три дня назад я провёл самый одинокий день рождения в своей жизни, мне уже 26 лет, и я действительно всё больше и больше теряюсь в жизни. Не знаю, когда ещё буду таким простодушным и добрым, не знаю, где моё будущее, но сейчас я уже не прошу ничего, кроме как охранять свет в своём сердце навсегда. Вернёмся к плану разработки: изначально планировалось добавить систему воспроизведения аудио, в настоящее время добавлены i2s-контроллер и кодек wm8750, но ядро не имеет готового драйвера, его нужно писать самому, сейчас фреймворк написан, но в qemu обнаружена проблема с основным pulseaudio, не очень удобно отлаживать, временно отключил устройство и отправил код.

2022.04.19 (вечером): Сегодня вышла последняя версия qemu 7.0.0, которая принесла много обновлений, связанных с riscv, включая расширения rvv и rvh, поддержку kvm. Мы сразу же перенесли реализацию quard-star в версию 7.0.0. Изменений в API не так много, в основном они направлены на поддержку kvm и добавление более стандартных rvclint и rvplic. На данный момент мы не реализуем поддержку kvm, сначала сделаем так, чтобы вся существующая функциональность работала нормально, а затем, на выходных, оптимизируем код. Также я ранее упоминал проблему с начальным значением pmp_state.addr, которую уже решили разработчики, поэтому мы можем удалить код, связанный с csrw pmpcfg0, 0 в mask_rom. После переноса и тестирования выяснилось, что в коде freertos, работающем на cpu7, возникла проблема, из-за которой невозможно продолжить планирование, эта проблема пока приостановлена, и мы будем исследовать её позже.

2022.06.05 (после полудня): Добавлен интерфейс onenand ip для изучения содержимого, связанного с nand flash, а также ubifs. Мы используем эту модель qemu-7.0.0/hw/block/onenand.c и добавляем её в наш soc quard star. Обратите внимание, что размер регистра пространства в этом IP составляет 0x20000. Здесь следует отметить, что имитируемый размер флэш-памяти составляет 256M, но при запуске с параметром -drive if=mtd,bus=1,unit=0,format=raw,file=$SHELL_FOLDER/output/fw/nandflash.img,id=mtd2 файл образа должен быть 264M, потому что драйвер будет использовать последние 8M пространства резервной области для хранения информации о плохих секторах.

2022.06.08 (вечером): Добавлен xlnx-can ip, особых проблем не возникло, но при тестировании хоста с использованием vcan для связи с can на гостевой системе обнаружилось, что порядок байтов данных пакетов кажется неправильным. Поэтому мы сравнили код ядра и код qemu и обнаружили, что формат сообщения xlnx can не соответствует формату socket can, требуется преобразование. Здесь можно найти эту проблему, прочитав Table 20‐3: CAN Message Format в «ug1085-zynq-ultrascale-trm.pdf». Удивительно, но реализация xlnx can в qemu-7.0.0/hw/net/can/xlnx-zynqmp-can.c, похоже, не преобразует формат сообщения Xilinx CAN, а напрямую пересылает формат Socket CAN, что приводит к ошибке. Здесь я добавил или изменил следующие функции, и проблема была решена (здесь я не отправлял патч в вышестоящую ветку qemu, потому что не уверен, действительно ли zynq также имеет эту проблему):

#define XCAN_IDR_IDE_MASK       0x00080000U
#define XCAN_IDR_ID1_MASK       0xFFE00000U
#define XCAN_IDR_ID2_MASK       0x0007FFFEU
#define XCAN_IDR_RTR_MASK       0x00000001U
#define XCAN_IDR_SRR_MASK       0x00100000U
#define XCAN_IDR_ID1_SHIFT      21
#define XCAN_IDR_ID2_SHIFT      1
#define CAN_SFF_ID_BITS         11
#define CAN_EFF_ID_BITS         29

static uint32_t id_xcan2can(uint32_t id)
{
    uint32_t ret_id = 0; 
    /* Change Xilinx CAN ID format to socketCAN ID format */
    if (id & XCAN_IDR_IDE_MASK) {
        /* The received frame is an Extended format frame */
        ret_id = (id & XCAN_IDR_ID1_MASK) >> 3;
        ret_id |= (id & XCAN_IDR_ID2_MASK) >>
                XCAN_IDR_ID2_SHIFT;
        ret_id |= QEMU_CAN_EFF_FLAG;
        if (id & XCAN_IDR_RTR_MASK)
            ret_id |= QEMU_CAN_RTR_FLAG;
    } else {
        /* The received frame is a standard format frame */
        ret_id = (id & XCAN_IDR_ID1_MASK) >>
                XCAN_IDR_ID1_SHIFT;
        if (id & XCAN_IDR_SRR_MASK)
            ret_id |= QEMU_CAN_RTR_FLAG;
    }
    return ret_id;
}

static uint32_t id_can2xcan(uint32_t id)
{
    uint32_t ret_id = 0;
    if (id & QEMU_CAN_EFF_FLAG) {
        /* Extended CAN ID format */
        ret_id = ((id & QEMU_CAN_EFF_MASK) << XCAN_IDR_ID2_SHIFT) &
            XCAN_IDR_ID2_MASK;
        ret_id |= (((id & QEMU_CAN_EFF_MASK) >>
            (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)) <<
            XCAN_IDR_ID1_SHIFT) & XCAN_IDR_ID1_MASK;
        ret_id |= XCAN_IDR_IDE_MASK | XCAN_IDR_SRR_MASK;
        if (id & QEMU_CAN_RTR_FLAG)
            ret_id |= XCAN_IDR_RTR_MASK;
    } else {
        /* Standard CAN ID format */
        ret_id = ((id & QEMU_CAN_SFF_MASK) << XCAN_IDR_ID1_SHIFT) &
            XCAN_IDR_ID1_MASK;
        if (id & QEMU_CAN_RTR_FLAG)
            ret_id |= XCAN_IDR_SRR_MASK;
    }
    return ret_id;
}

static void generate_frame(qemu_can_frame *frame, uint32_t *data)
{
    frame->can_id = id_xcan2can(data[0]);
    frame->can_dlc =
``` **FIELD_EX32(data[1], TXFIFO_DLC, DLC);**

    frame->data[0] = FIELD_EX32(data[2], TXFIFO_DATA1, DB0);
    frame->data[1] = FIELD_EX32(data[2], TXFIFO_DATA1, DB1);
    frame->data[2] = FIELD_EX32(data[2], TXFIFO_DATA1, DB2);
    frame->data[3] = FIELD_EX32(data[2], TXFIFO_DATA1, DB3);

    frame->data[4] = FIELD_EX32(data[3], TXFIFO_DATA2, DB4);
    frame->data[5] = FIELD_EX32(data[3], TXFIFO_DATA2, DB5);
    frame->data[6] = FIELD_EX32(data[3], TXFIFO_DATA2, DB6);
    frame->data[7] = FIELD_EX32(data[3], TXFIFO_DATA2, DB7);
}

static void update_rx_fifo(XlnxZynqMPCANState *s, const qemu_can_frame *frame) {
    bool filter_pass = false;
    uint16_t timestamp = 0;

    /* If no filter is enabled. Message will be stored in FIFO. */
    if (!((ARRAY_FIELD_EX32(s->regs, AFR, UAF1)) |
        (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) |
        (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) |
        (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)))) {
        filter_pass = true;
    }

    /*
    * Messages that pass any of the acceptance filters will be stored in
    * the RX FIFO.
    */
    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF1)) {
        uint32_t id_masked = s->regs[R_AFMR1] & frame->can_id;
        uint32_t filter_id_masked = s->regs[R_AFMR1] & s->regs[R_AFIR1];

        if (filter_id_masked == id_masked) {
            filter_pass = true;
        }
    }

    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) {
        uint32_t id_masked = s->regs[R_AFMR2] & frame->can_id;
        uint32_t filter_id_masked = s->regs[R_AFMR2] & s->regs[R_AFIR2];

        if (filter_id_masked == id_masked) {
            filter_pass = true;
        }
    }

    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) {
        uint32_t id_masked = s->regs[R_AFMR3] & frame->can_id;
        uint32_t filter_id_masked = s->regs[R_AFMR3] & s->regs[R_AFIR3];

        if (filter_id_masked == id_masked) {
            filter_past = true;
        }
    }

    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) {
        uint32_t id_masked = s->regs[R_AFMR4] & frame->can_id;
        uint32_t filter_id_masked = s->regs[R_AFMR4] & s->regs[R_AFIR4];

        if (filter_id_masked == id_masked) {
            filter_pass = true;
        }
    }

    if (!filter_pass) {
        trace_xlnx_can_rx_fifo_filter_reject(frame->can_id, frame->can_dlc);
        return;
    }

    /* Store the message in fifo if it passed through any of the filters. */
    if (filter_pass && frame->can_dlc <= MAX_DLC) {

        if (fifo32_is_full(&s->rx_fifo)) {
            ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 1);
        } else {
            timestamp = CAN_TIMER_MAX - ptimer_get_count(s->can_timer);

            fifo32_push(&s->rx_fifo, id_can2xcan(frame->can_id));

            fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DLC_DLC_SHIFT,
                                            R_RXFIFO_DLC_DLC_LENGTH,
                                            frame->can_dlc) |
                                    deposit32(0, R_RXFIFO_DLC_RXT_SHIFT,
                                            R_RXFIFO_DLC_RXT_LENGTH,
                                            timestamp));

            /* First 32 bit of the data. */
            fifo32_push(&s->rx_fifo, deposit32(0, R_TXFIFO_DATA1_DB0_SHIFT,
                                            R_TXFIFO_DATA1_DB0_LENGTH,
                                            frame->data[0]) |
                                    deposit32(0, R_TXFIFO_DATA1_DB1_SHIFT,
                                            R_TXFIFO_DATA1_DB1_LENGTH,
                                            frame->data[1]) |
                                    deposit32(0, R_TXFIFO_DATA1_DB2_SHIFT,
                                            R_TXFIFO_DATA1_DB2_LENGTH,

В запросе представлен фрагмент кода на языке C, который выполняет операции с данными и регистрами в контексте разработки программного обеспечения для микроконтроллеров. В тексте используются специфические функции и структуры данных, связанные с работой с аппаратными ресурсами микроконтроллера. ```c
// SPDX-License-Identifier: GPL-2.0+

#define LOG_CATEGORY UCLASS_VIDEO

#include <common.h>
#include <clk.h>
#include <display.h>
#include <dm.h>
#include <log.h>
#include <panel.h>
#include <reset.h>
#include <video.h>
#include <video_bridge.h>
#include <asm/io.h>
#include <asm/arch/gpio.h>
#include <dm/device-internal.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <config.h>

#define VIDEO_PL111_MAX_XRES (1024)
#define VIDEO_PL111_MAX_YRES (768)
#define VIDEO_PL111_MAX_BPP  (24)

struct pl111_priv {
    void __iomem *regs;
    enum video_log2_bpp l2bpp;
};

/* PL111 main registers */
#define CLCD_TIM0       0x00000000
#define CLCD_TIM1       0x00000004
#define CLCD_TIM2       0x00000008
#define CLCD_TIM3       0x0000000c
#define CLCD_UBAS       0x00000010
#define CLCD_LBAS       0x00000014

#define CLCD_PL111_CNTL     0x00000018
#define CLCD_PL111_IENB     0x0000001c
#define CLCD_PL111_RIS      0x00000020
#define CLCD_PL111_MIS      0x00000024
#define CLCD_PL111_ICR      0x00000028
#define CLCD_PL111_UCUR     0x0000002c
#define CLCD_PL111_LCUR     0x00000030

#define CNTL_LCDEN      (1 << 0)
#define CNTL_LCDBPP1        (0 << 1)
#define CNTL_LCDBPP2        (1 << 1)
#define CNTL_LCDBPP4        (2 << 1)
#define CNTL_LCDBPP8        (3 << 1)
#define CNTL_LCDBPP16       (4 << 1)
#define CNTL_LCDBPP16_565   (6 << 1)
#define CNTL_LCDBPP16_444   (7 << 1)
#define CNTL_LCDBPP24       (5 << 1)
#define CNTL_LCDBW      (1 << 4)
#define CNTL_LCDTFT     (1 << 5)
#define CNTL_LCDMONO8       (1 << 6)
#define CNTL_LCDDUAL        (1 << 7)
#define CNTL_BGR        (1 << 8)
#define CNTL_BEBO       (1 << 9)
#define CNTL_BEPO       (1 << 10)
#define CNTL_LCDPWR     (1 << 11)
#define CNTL_LCDVCOMP(x)    ((x) << 12)
#define CNTL_LDMAFIFOTIME   (1 << 15)
#define CNTL_WATERMARK      (1 << 16)

enum pl111_pix_fmt {
    PF_BPP1 = 0,
    PF_BPP2,
    PF_BPP4,
    PF_BPP8,
    PF_BPP16,
    PF_BPP24,
    PF_BPP16_565,
    PF_BPP12_444
};

static u32 pl111_get_pixel_format(enum video_log2_bpp l2bpp)
{
    enum pl111_pix_fmt pf;

    switch (l2bpp) {
        case VIDEO_BPP1:
            pf = PF_BPP1;
            break;
        case VIDEO_BPP2:
            pf = PF_BPP2;
            break;
        case VIDEO_BPP4:
            pf = PF_BPP4;
            break;
        case VIDEO_BPP8:
            pf = PF_BPP8;
            break;
        case VIDEO_BPP16:
            pf = PF_BPP16_565;
            break;
        case VIDEO_BPP32:
        default:
            log_warning("warning %dbpp not supported yet, %dbpp     instead\n",
                        VNBITS(l2bpp), VNBITS(VIDEO_BPP16));
            pf = PF_BPP16_565;
            break;
    }

    log_debug("%d bpp -> ltdc pf %d\n", VNBITS(l2bpp), pf);

    return (u32)pf;
}

static void pl111_enable(struct pl111_priv *priv)
{
    size_t regs_addr = (size_t)priv->regs;
    setbits_le32(regs_addr + CLCD_PL111_CNTL, CNTL_LCDPWR);
}

static void pl111_set_mode(struct pl111_priv *priv,
                           struct display_timing *timings, ulong     fb_addr)
{
    size_t regs_addr = (size_t)priv->regs;
    u32 ppl, hsw, hfp, hbp;
    u32 lpp, vsw, vfp, vbp;
    u32 format;

    ppl = (timings->hactive.typ / 16) - 1;
    hsw = timings->hsync_len.typ;
    hfp = timings->hfront_porch.typ;
    hbp = timings->hback_porch.typ;

    lpp = timings->vactive.typ - 1;
    vsw = timings->vsync_len.typ;
    vfp = timings->vfront_porch.typ;
    vbp = timings->vback_porch.typ;

    writel((ppl << 2) | (hsw << 8) | (hfp << 16) | (hbp <<     24),
           regs_addr +

Это похоже на фрагмент кода на языке C, который описывает реализацию функций для работы с видеокартой PL111 в системе UBOOT. В коде используются структуры данных и функции для настройки параметров видеокарты, таких как разрешение экрана, частота обновления и формат пикселей. Также код включает в себя функции для включения и настройки режима работы видеокарты.

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

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/QQxiaoming-quard_star_tutorial.git
git@api.gitlife.ru:oschina-mirror/QQxiaoming-quard_star_tutorial.git
oschina-mirror
QQxiaoming-quard_star_tutorial
QQxiaoming-quard_star_tutorial
main