Привет, Хабр! В этом цикле статей я попытаюсь наглядно и сжато объяснить устройство встраиваемых систем на базе Rockchip. Пройдусь по всем шагам загрузки, начиная с первой инструкции и заканчивая разворачиванием всей системы. Для демонстрации я выбрал плату Orange Pi R1 Plus LTS на базе Rockchip RK3328 SoC, ARM Cortex-A53 64-Bit Processor.

Orange Pi R1 Plus LTS
Orange Pi R1 Plus LTS

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

Схема RK3328 SOC
Схема RK3328 SOC

Общение процессора (CPU Cortex-A53) с периферией основано на обращении к памяти. Для этого процессор использует Memory-Mapped I/O (MMIO). Модель памяти процессора условно разделена на сектора, часть из которых зарезервирована под периферию.

Для примера возьмем интерфейс UART. Записывая и считывая данные по определенному адресу, можно общаться с физическим устройством — контроллером UART. Общение происходит по набору шин, к которому подключены все доступные процессору устройства.

Общая схема системной шины
Общая схема системной шины

На самом деле архитектура шин RK3328 имеет более сложное устройство и отличается от схемы выше. В RK3328 используются не одна, а несколько оптимизированных шин для различных видов устройств, а также внутренние протоколы передачи данных. RK3328 использует набор шин AMBA (Advanced Microcontroller Bus Architecture). Внутри этого набора существуют:

  • AXI (Advanced eXtensibility Interface) — основная скоростная магистраль. Обеспечивает быструю связь между процессорами (CPU).

  • AHB (Advanced High-performance Bus) — высокопроизводительная шина. Применяется для контроллеров памяти, DMA.

  • APB (Advanced Peripheral Bus) — медленная периферийная шина.

Для подробного ознакомления можно посмотреть Technical Reference Manual для RK3328. Вот так выглядит схема контроллера UART: он подключен к медленной APB-шине, которая, в свою очередь, соединена с быстрой AXI. На схеме можно увидеть ресивер и трансмиттер, отвечающие за физическое кодирование и декодирование информации, блоки буфера и тактирования. Всё это выводится в блок регистров для взаимодействия с CPU через интерфейс ABP-шины. Регистры — это и есть те адреса памяти, по которым приходят запросы от CPU. Благодаря им контроллер UART ведет себя как ячейка памяти, но с динамически меняющимися значениями.

Архитектура UART RK3328
Архитектура UART RK3328

Ознакомиться с адресами и регистрами памяти также можно прочитав Technical Reference Manual для RK3328. В разделе Address Mapping можно увидеть, что UART1 имеет адрес FF13_0000, а также регистры, адреса которых имеют смещение относительно базового адреса.

 Memory-map для RK3328
Memory-map для RK3328
Регистры UART RK3328
Регистры UART RK3328

Для сравнения, ниже приведен пример распределения адресов памяти для другого процессора — Xtensa на базе ESP32.

Memory-map для ESP32
Memory-map для ESP32

На карте видно, что через адреса памяти процессор получает доступ к внешней памяти (external flash/sram). Для преобразования виртуального адреса в физический используется Memory Management Unit (MMU). Получая запрос на определенный адрес, он конвертирует его во внутренний физический адрес хранилища, читает данные и отдает их обратно, как если бы это был настоящий адрес памяти.

Для каждой архитектуры процессора есть свой ассемблер. В данном случае процессор Cortex-A53 имеет архитектуру ARM V8-A. Архитектура ARM изначально создавалась для использования в мобильных устройствах и встраиваемых системах, поэтому набор команд и регистров облегчен по сравнению с x86. В качестве демонстрации попробуем написать программу для этой архитектуры.

.section .text
.global _start

.equ UART_BASE, 0xff130000      // Базовый адрес UART2
.equ UART_THR,  0x00            // Отступ регистра передачи
.equ UART_LSR,  0x14            // Отступ регистра состояния линии
.equ LSR_THRE,  (1 << 5)        // Маска регистра говности к приему (5 бит) 

_start:
    adr     x0, msg             // Загрузка сообщения в относительный адрес памяти

print_loop:
    ldrb    w1, [x0], #1        // Загрузить 1 байт сообщения из x0 в w1, затем увеличить x0 на 1
    cbz     w1, done            // Проверить w1 на конец строки (0) 

wait_uart:
    ldr     x2, =UART_BASE      // Поместить в x2 базовый адрес UART2
    ldr     w3, [x2, #UART_LSR] // Записать в w3 значение регистра UART_LSR
    tst     w3, #LSR_THRE       // Если значение UART_LSR true, UART готов к приему
    beq     wait_uart           // Если значение UART_LSR false повторить

    str     w1, [x2, #UART_THR] // Записать значение w1 в регистр передачи
    b       print_loop          // Запустить функцию заново

done:
    ret                         // Возврат

.section .data
msg:
    .asciz "Hello from RK3328!\r\n" //Строка, заканчивющаяся 0

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

Скомпилируем программу, используя компилятор aarch64-linux-gnu-as

aarch64-linux-gnu-as -o hello.o hello.S
aarch64-linux-gnu-ld -Ttext 0x00200000 -o hello.elf hello.o
aarch64-linux-gnu-objcopy -O binary hello.elf hello.bin

*Примечание: адрес 0x00200000 используется, чтобы указать программе начальный адрес исполнения. Для правильной работы программу нужно будет загрузить именно по этому адресу.

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

В следующей части я расскажу, как программа попадает в память, о процессе загрузки и загрузчиках. Увидимся.

Полезные материалы:

[1]: Orange Pi R1 Plus LTS

[2]: Rockchip RK3328 Technical Reference Manual

[3]: ARMv8 Instruction Set

Комментарии (5)


  1. REPISOT
    29.12.2025 05:43

    На мой взгляд, кусок кода (тем более на ассемблере) во вводной части неуместен.


  1. Albert2009Zi
    29.12.2025 05:43

    Вся суть статьи сводится к одной фразе - читайте юзер мануал на рокчип. И почему не начать по классике с Blink на Си? Вы знаете многих начинающих, пишущих на Асме?

    Про компиляцию программы вообще шедеврально написано. Многие миддлы компилируют прошивки через нативные IDE от производителей процов и не заморачиваются кросскомпилированием в терминале. А вы хотите не расписав самого процесса (допустим из голой Linux без пакетов), подсунуть три строчки компиляции и линковки новичку. Считаете получится?

    Ну и вишенка - "А как залить это все на плату читайте в следующей статье"... Думаете, сильно популярным станет такое повествование? Ведь весь интерес у начинающего, получить какой-то результат от первых шагов. В том и прелесть, допустим, у того же Ардуино.


    1. cmon Автор
      29.12.2025 05:43

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

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

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

      Возможно, Вы правы в том, что стоит добавить код на C рядом с ASM. Что до результата, то постараюсь Вас не разочаровать. Проявите немного терпения. Это только первая часть. Спасибо за Ваш комментарий.


    1. Coder007
      29.12.2025 05:43

      Все верно! Начинать от простого к сложному! Постепенно, кушая слона по частям, начиная с маленьких кусочков.


  1. Coder007
    29.12.2025 05:43

    Что из этого, начинающий должен был понять? Что-то про регистры процессора? Про прерывания? Про команды? Про что? Кусок незаконченной статьи, текст ничем, информации - нет. Читал, смотрел и статья закончилась. Неожиданно. Это ещё и часть 1! Я боюсь представить, что будет в части 2! Там на асмемблере будем писать операционку? Автор, ты молодец, что хочешь поделиться чем-то, но попробуй структурировать подачу информации. Ты пишешь : для начинающих, а я с опытом ассемблера и работая с этими одноплатниками тебя не понял!