Я разрабатываю тестовое ядро для устройств 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 должен откладываться на изменчивую инструкцию по сборке.
0x20200000
, но не выходит ли из строя какой-то самый первый mmio_write, если UART находится на другом? Или, может быть, если запись терпит неудачу, просто молча игнорируется, есть какое-то значение, которое можно прочитать обратно после записи, поэтому на недопустимом порту UART оно не будет изменено, и вы можете вернуть его как сбой? - person Ped7g   schedule 07.02.2017