Как определить версию платы Rasberry Pi с помощью Arm Assembly/C?

Я разрабатываю тестовое ядро ​​для устройств Raspberry Pi. При этом мне нужно настроить UART, чтобы мы могли записывать данные на устройство и извлекать данные, которые должны обрабатываться ядром. Я хочу, чтобы тестовое ядро ​​могло работать на нескольких устройствах Raspberry Pi. Однако есть небольшая проблема:

Адреса UART различаются в разных версиях. Например, адрес для RPi 1 линии UART GPIO:

0x20200000

Но адрес для UART GPIO на линиях RPi 2 и RPi 3:

0x3F200000

Естественно, это означает, что должны быть две отдельные функции UART_INIT: 1 для линейных устройств RPi 1 и 1 для RPi 2 и выше.

Вот пример кода обработки UART. Этот код изменен по сравнению с кодом, предоставленным osdev:

void uart_init_rpi1()
{
    // Disable UART0.
    mmio_write(PD_UART_INTERNAL_VALUES_RPI1->UART0_CR, 0x00000000);
    // Setup the GPIO pin 14 && 15.

    // Disable pull up/down for all GPIO pins & delay for 150 cycles.
    mmio_write(PD_GPPUD_RPI1, 0x00000000);
    delay(150);

    // Disable pull up/down for pin 14,15 & delay for 150 cycles.
    mmio_write(PD_GPPUDCLK0_RPI1, (1 << 14) | (1 << 15));
    delay(150);

    // Write 0 to GPPUDCLK0 to make it take effect.
    mmio_write(PD_GPPUDCLK0_RPI1, 0x00000000);

    // Clear pending interrupts.
    mmio_write(PD_UART_INTERNAL_VALUES_RPI1->UART0_ICR, 0x7FF);

    // Set integer & fractional part of baud rate.
    // Divider = UART_CLOCK/(16 * Baud)
    // Fraction part register = (Fractional part * 64) + 0.5
    // UART_CLOCK = 3000000; Baud = 115200.

    // Divider = 3000000 / (16 * 115200) = 1.627 = ~1.
    // Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40.
    mmio_write(PD_UART_INTERNAL_VALUES_RPI1->UART0_IBRD, 1);
    mmio_write(PD_UART_INTERNAL_VALUES_RPI1->UART0_FBRD, 40);

    // Enable FIFO & 8 bit data transmissio (1 stop bit, no parity).
    mmio_write(PD_UART_INTERNAL_VALUES_RPI1->UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6));

    // Mask all interrupts.
    mmio_write(PD_UART_INTERNAL_VALUES_RPI1->UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) |
                       (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10));

    // Enable UART0, receive & transfer part of UART.
    mmio_write(PD_UART_INTERNAL_VALUES_RPI1->UART0_CR, (1 << 0) | (1 << 8) | (1 << 9));
}

Существует вторичная аналогичная функция для обработки UART INIT на линии RPi 2 и RPi 3. Наличие двух отдельных функций UART INIT — это нормально, и это не проблема. Проблема заключается в том, чтобы различать типы плат. это избавило бы меня от многих хлопот, если бы был способ определить текущую используемую плату. Отсутствие возможности означает, что мне нужно сделать отдельное тестовое ядро ​​для плат RPi 1, плат RPi 2-3 и любых других плат, подобных RPi, таких как, например, ODROID OC-2. Если бы каким-то образом был способ определить тип платы, я мог бы использовать его в качестве условия и загружать правильные значения UART при загрузке, а это означает, что нужен только один единственный файл ядра. Один из способов, который может сработать, — это тестирование на основе процессора, который уникален для каждой версии RPi и альтернативных плат. На платформах x86 вы можете использовать _RTDSC, но я совершенно уверен, что таких альтернатив не существует на процессорах НЕ-x86/x86-64.

Итак, вопрос, который я задаю: есть ли способ, либо на ассемблере, либо на C, позволяющий проверить, на каком типе платы работает пользователь/код? Поскольку я создаю ядро ​​​​ОС, у меня нет доступа к библиотекам C, не являющимся компиляторами, поэтому код C должен откладываться на изменчивую инструкцию по сборке.


person Alexander A. J. Frankland    schedule 07.02.2017    source источник
comment
Затем взгляните на существующий модуль ядра Linux, отвечающий за создание этой информации. Но я не очень понимаю, как вы создаете переносимое ядро ​​для разных версий, так как UART действительно не единственное и главное отличие. Разные RP имеют разные процессоры   -  person Eugene Sh.    schedule 07.02.2017
comment
@inifinitelyManiac пользователь будет человеком с тестовым ядром на mSDHC. например, у меня RPi 3b, а у моего друга RPi 1. Я искал способ, чтобы мне не пришлось перекомпилировать тестовое ядро ​​для каждой вариации платы. Я думаю, я должен был быть немного более конкретным там.   -  person Alexander A. J. Frankland    schedule 07.02.2017
comment
@Юджин ш. Используемый компилятор является общей целью: gcc-arm-none-eabi. Я хорошо знаю внутренние различия между платами, но все они используют сборку Arm. Исполняемый файл ядра является переносимым только в этом смысле.   -  person Alexander A. J. Frankland    schedule 07.02.2017
comment
Если процессоры разные, вы можете проверить это вместо этого? Другой (более надежный и очевидный IMO) способ - просто попытаться инициализировать разные UART до тех пор, пока один из них не добьется успеха. Из исходного примера неясно, где используется этот 0x20200000, но не выходит ли из строя какой-то самый первый mmio_write, если UART находится на другом? Или, может быть, если запись терпит неудачу, просто молча игнорируется, есть какое-то значение, которое можно прочитать обратно после записи, поэтому на недопустимом порту UART оно не будет изменено, и вы можете вернуть его как сбой?   -  person Ped7g    schedule 07.02.2017
comment
В прошивке есть «почтовый ящик», куда можно отправить запрос для получения этой информации. Читать Версия платы RPI и размер памяти, а также источник U-boot, который Linux действительно делегирует для этого в текущей версии Linux. (пожалуйста, проголосуйте, если это полезно, чтобы это было выше других комментариев)   -  person artless noise    schedule 07.02.2017
comment
есть / есть регистры ARM cpuid, которые вы можете прочитать, чтобы отличить процессоры pi1, pi2, pi3, из которых вы можете определить, является ли это 0x20xxxxxx или 0x3Fxxxxxx.   -  person old_timer    schedule 08.02.2017


Ответы (2)


Поскольку каждое поколение pi использует другое ядро ​​​​процессора от ядра (конкретный armv6, затем armv7, а затем armv8), вы можете легко обнаружить их по регистру идентификатора процессора.

.globl GETCPUID
GETCPUID:
    mrc p15,0,r0,c0,c0,0
    bx lr

Разные ядра возвращают разные значения

PI3 0x410FD034
PI2 0x410FC075
PI1/Zero 0x410FB767

И оттуда вы можете установить свою периферийную базу

if((id&0xFFFFFFFF)==0x410FB767) PBASE=0x20000000;
else                            PBASE=0x3F000000;
person old_timer    schedule 09.02.2017

Ядра ARM для RP1, RP2 и RP2 v1.2 (далее) отличаются, а именно. ARM11, Cortex-A7 и Cortex-A53.

Но ARM CP15 имеет информацию об архитектуре, номере детали и версии ядра, и, согласно документации ARM, одна и та же инструкция может получить сведения об обоих ядрах.

MRC p15,0,<Rd>,c0,c0,0; reads Main ID register

ARM11 вернет номер детали в виде 0xB76
Cortex-A7 выдаст 0xC07
, а Cortex-A53 выдаст 0xD03.

Пожалуйста, смотрите следующие две ссылки с infocenter.arm (я не смог добавить больше двух ссылок, поэтому предоставляю ссылки только на старые и последние)

Ссылки:
ARM1176JZF-S(PI-1):
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0301h/Bgbiddeb.html

Cortex-A53 (PI-2 версии 1.2 и выше): http://infocenter.arm.com/help/topic/com.arm.doc.ddi0500g/BABFEABI.html

Надеюсь, поможет.

[Спасибо старожилу за исправления]

person RuSh    schedule 08.02.2017
comment
pi2 и p3 — разные ядра, pi2 — это armv7, pi3 — armv8 (64-разрядная версия). - person old_timer; 09.02.2017
comment
@old-timer спасибо за ваш вклад. Я исправил ответ. - person RuSh; 09.02.2017
comment
обратите внимание, что pizero представляет собой уменьшенный pi1, поэтому он использует тот же чип, что и pi1, и тот же базовый адрес периферии в адресном пространстве руки. - person old_timer; 09.02.2017