Skip to content

porting_mips_ru

Serge Vakulenko edited this page Aug 26, 2015 · 2 revisions

Микроконтроллеры Элвис Мультикор

Описана последовательность работ по переносу системы uOS на архитектуру MIPS32, применительно к микроконтроллеру MC-24 семейства Элвис Мультикор.

Скалярное ядро процессоров Элвис Мультикор имеет архитектуру MIPS32, новую для uOS. В первую очередь требуется реализация машинно-зависимой части микроядра для архитектуры MIPS32. Затем необходимо реализовать работу с периферийными устройствами: инициализацию, таймер, UART и т.д.

1. Создаём каталог целевой платформы

Создаем каталог uos/examples/mips-mc24. Помещаем туда файлы, необходимые для компиляции.

mkdir uos/examples/mips-mc24
cd uos/examples/mips-mc24
cp ../arm-s3c4530/target.cfg .
cp ../arm-s3c4530/ldscript.x .

В файле target.cfg изменяем значения переменных:

  • ARCH - mips.
  • MODULES - необходимо убрать s3c4530.
  • CFLAGS - задаем тип платформы -DMIPS32 и модель конкретного микроконтроллера -DELVEES_MC24.
  • BINDIR - устанавливаем правильный путь к компилятору, в моём случае это /usr/local/mipsel432/bin.
  • CC, AR и прочие - устанавливаем имена компонентов компилятора: mipsel-elf32-gcc и т.п.
  • STARTUP - стартовый код разместим в файле startup-mc24.S.

В файле ldscript.x заменяем везде arm на mips, а также в блоке MEMORY изменяем адреса и длины сегментов памяти:

  • Для text устанавливаем ORIGIN = 0xbfc00000, LENGTH = 2M
  • Для data устанавливаем ORIGIN = 0xb8000000, LENGTH = 32K

В сегменте .data после data_start добавляем установку адреса для регистра $gp. Поскольку в контроллере МС-24 имеется всего 32 килобайта внутренней памяти, смещение на 0x8000 не требуется:

_gp = .;

2. Создаём файл описания специальных регистров процессора

Создаём каталог uos/sources/runtime/mips и в нём include-файл io.h с описанием регистров платформы MIPS32. Для конкретного процессора МС-24 делаем отдельный файл io-mc24.h. Файл io.h имеет вид:

#ifdef ELVEES_MC24
#   include <runtime/mips/io-mc24.h>
#endif

В дальнейшем сюда можно добавлять варианты для других процессоров с архитектурой MIPS32.

В файл io.h также помещаем определения inline-функций для доступа к аппаратным регистрам:

  • int mips_get_stack_pointer () - чтение значения регистра стека SP
  • void mips_set_stack_pointer (int) - установка значения регистра стека SP
  • int mips_read_c0_register (int) - чтение регистров сопроцессора C0
  • void mips_write_c0_register (int, int) - запись регистров сопроцессора C0
  • void mips_intr_disable (int *) - сохранение статуса и запрет прерываний
  • void mips_intr_restore (int) - восстановление статуса прерываний
  • void mips_intr_enable () - разрешение прерываний

3. Проектируем структуру контекста

Определяем количество и порядок регистров, сохраняемых в стеке при входе в прерывание или исключение. Документируем на будущее.

4. Создаём стартовый файл

Создаём стартовый файл startup-mc24.S. Стартовый код размещаем в секции .init, которая будет находиться по адресу 0xbfc00000. Вектора прерываний:

  • bfc00000 - аппаратный сброс, немаскируемое прерывание. Вызов Си-функции _init_().
  • bfc00200 - обращение к отсутствующей странице. Сохранение регистров в стеке и вызов Си-функции _pagefault_handler_(context).
  • bfc00380 - исключение. Сохранение регистров в стеке и вызов Си-функции _exception_handler_(context).
  • bfc00400 - прерывание. Сохранение регистров в стеке и вызов Си-функции _interrupt_handler_(context). Здесь int *context указывает на массив из 32-х сохранённых регистров.

5. Делаем функцию начальной инициализации

Создаём файл uos/sources/runtime/mips/init.c, содержащий функцию _init_(). Она должна:

  • устанавливать начальное состояние регистров микроконтроллера;
  • копировать инициализированные данные из памяти Flash в ОЗУ;
  • обнулять сегмент BSS;
  • вызывать функцию main(). Как образец можно использовать аналогичный файл для архитектуры ARM: runtime/arm/init.c.

6. Создаём обработчики исключений

В файл init.c добавляем функции _pagefault_handler_(context) и _exception_handler_(context). Они должны печатать на отладочную консоль значения регистров и выполнять аппаратный сброс системы.

7. Создаем архитектурозависимые файлы стандартной библиотеки

Создаём файлы sources/runtime/mips/types.h, sources/runtime/mips/stdlib.h и sources/runtime/mips/string.h:

cd uos/sources/runtime/mips/
cp ../arm/types.h ../arm/stdlib.h ../arm/string.h .

В начале файла sources/runtime/arch.h в соответствующее место добавляем фрагмент:

#elif defined (MIPS32)
#       include <stdarg.h>
#       include <runtime/mips/types.h>
#       include <runtime/mips/string.h>
#       include <runtime/mips/stdlib.h>
#       include <runtime/mips/io.h>
#       include <runtime/ctype.h>
#       define __BYTE_ORDER __LITTLE_ENDIAN
#       define __FLOAT_WORD_ORDER __LITTLE_ENDIAN

Теперь можно проверить, что файл init.c правильно компилируется:

/usr/local/mipsel432/bin/mipsel-elf32-gcc -c -fno-builtin -DMIPS32 -DELVEES_MC24 -I../.. init.c

8. Делаем функции отладочной выдачи на консоль

В файле uos/sources/runtime/mips/debug.c создаём функции отладочной выдачи на консоль: debug_putchar(), debug_getchar(), debug_peekchar(). Заключаем их в #ifdef ELVEES_MC24 ... #endif.

9. Первый тест: отладочная выдача на консоль

В каталоге uos/examples/mips-mc24 создаём файлы test_debug.c:

#include <runtime/lib.h>

int main (void)
{
        for (;;) {
                debug_printf ("Hello, World!\n");
                debug_getchar();
        }
}

void _interrupt_handler_ ()
{
}

и Makefile:

OS              = $(shell cd ../..; pwd)
TARGET          = $(OS)/examples/mips-mc24
include $(TARGET)/target.cfg

TESTS           = test_debug.hex
OUTS            = $(TESTS:%.hex=%.out)

all:            .deps target $(OUTS) $(TESTS)
                $(SIZE) $(OUTS)
.deps:
                mkdir .deps
target:
                $(MAKE) -C $(TARGET)
clean:
                rm -rf *.out *.hex *.sre *.[osi] *.lst *.dis *~ .deps

include $(OS)/sources/rules.mak

Запускаем "make". В результате получаем файл test_debug.hex, который загружаем в ПЗУ целевой платы. При включении на консоль должна выдаваться строка "Hello, World" (скорость 115200, 8 бит, без чётности).

10. Перенос ядра

Создаём каталог uos/sources/kernel/mips и в нём include-файл machdep.h с описанием машинно-зависимых функций и макросов для платформы MIPS32:

  • ARCH_INTERRUPTS - количество аппаратный прерываний, 40 для MC-24
  • arch_state_t - тип для хранения маски аппаратных прерываний, int для MIPS-32
  • arch_stack_t - тип для хранения указателя стека, void * для MIPS-32
  • arch_get_stack_pointer () - inline-функция, возвращающая текущий указатель стека
  • arch_set_stack_pointer () - inline-функция, устанавливающая указатель стека
  • arch_intr_disable () - inline-функция, запрещающая аппаратные прерывания
  • arch_intr_restore () - inline-функция, восстанавливающая режим аппаратных прерываний
  • arch_intr_bind () - inline-функция, пустая для MIPS-32
  • arch_intr_unbind () - inline-функция, пустая для MIPS-32
  • arch_idle () - inline-функция, переводящая процессор в режим ожидания

В файл machdep.c помещаем код машинно-зависимых функций:

  • arch_task_switch () - функция, переключающая выполнение на другую задачу
  • _interrupt_handler_ () - обработчик аппаратного прерывания, вызываемый из startup-кода
  • arch_intr_allow () - функция, открывающая маску указанного аппаратного прерывания
  • arch_build_stack_frame () - функция, создающая фрейм в стеке для старта новой задачи

11. Второй тест: формирование задач

В каталоге uos/examples/mips-mc24 создаём файл test_task.c:

#include <runtime/lib.h>
#include "kernel/uos.h"

ARRAY (task, 0x400);

void hello (void *arg)
{
        for (;;) {
                debug_printf ("Hello from `%s'!\n", arg);
                debug_printf ("Task space %d bytes, free %d bytes\n",
                        sizeof (task), task_stack_avail ((task_t*) task));
                debug_printf ("(Press Enter)\n");
                debug_getchar ();
        }
}

void uos_init (void)
{
        debug_puts ("\nTesting task.\n");
        task_create (hello, "task", "hello", 1, task, sizeof (task));
}

В файле Makefile устанавливаем TESTS = test_task.hex. Запускаем "make". В результате получаем файл test_task.hex, который загружаем в ПЗУ целевой платы. При включении на консоль должна выдаваться строка "Hello from `task'" и информация о свободной части стека задачи.

12. Драйвер таймера

В начале файла sources/timer/timer.c добавляем объявление номера прерывания таймера:

#if ELVEES_MC24
#   define TIMER_IRQ 29
#endif

В функции timer_init() в соответствующее место добавляем инициализацию регистров таймера:

#if ELVEES_MC24
        MC_ITCSR = 0;
        MC_ITSCALE = 0;
        MC_ITPERIOD = t->khz * t->msec_per_tick - 1;
        MC_ITCSR = MC_ITCSR_EN;
#endif

В обработчике прерывания timer_handler() сбрасываем признак прерывания:

#if ELVEES_MC24
        MC_ITCSR &= ~MC_ITCSR_INT;
#endif

13. Третий тест: таймер

В каталоге uos/examples/mips-mc24 создаём файл test_timer.c:

#include <runtime/lib.h>
#include "kernel/uos.h"
#include "timer/timer.h"

ARRAY (task, 0x400);
timer_t timer;

void hello (void *arg)
{
        for (;;) {
                debug_printf ("Hello from `%s'! msec = %d\n",
                        arg, timer_milliseconds (&timer));
                mutex_wait (&timer.decisec);
        }
}

void uos_init (void)
{
        debug_puts ("\nTesting timer.\n");
        timer_init (&timer, KHZ, 1000);
        task_create (hello, "task", "hello", 1, task, sizeof (task));
}

В файле Makefile устанавливаем TESTS = test_timer.hex. Запускаем "make". В результате получаем файл, который загружаем в ПЗУ целевой платы. При включении на консоль должна выдаваться строка "Hello from `task'" и счётчик миллисекунд.

14. Драйвер асинхронного порта

В начале файла sources/uart/uart.h в соответствующее место добавляем определение размера стека задачи асинхронного порта:

#if MIPS32
#   define UART_STACKSZ 0x400

Аналогично в файле протокола SLIP sources/uart/slip.h:

#if MIPS32
#   define SLIP_STACKSZ 0x400

В начале файлов sources/uart/uart.c и sources/uart/slip.c добавляем включение файла макросов для MC-24:

#if ELVEES_MC24
#   include "uart/elvees.h"
#endif

Создаём файл sources/uart/elvees.h с макросами UART для MC-24:

#include "kernel/internal.h"

#define RECEIVE_IRQ(p)  4               /* both receive and transmit */

#define enable_receiver(p)              /* already enabled in init() */
#define enable_receive_interrupt(p)     (MC_IER |= MC_IER_ERXRDY | MC_IER_ERLS)
#define disable_receive_interrupt(p)    (MC_IER &= ~(MC_IER_ERXRDY | MC_IER_ERLS))
#define enable_transmit_interrupt(p)    (MC_IER |= MC_IER_ETXRDY)
#define disable_transmit_interrupt(p)   (MC_IER &= ~MC_IER_ETXRDY)

#define transmit_byte(p,c)              (MC_THR = (c))
#define get_received_byte(p)            MC_RBR

#define test_transmitter_enabled(p)     1
#define test_transmitter_empty(p)       (MC_LSR & MC_LSR_TXRDY)
#define test_get_receive_data(p,d)      ((__uart_lsr & MC_LSR_RXRDY) ? \
                                        ((*d) = MC_RBR, 1) : 0)
#define test_frame_error(p)             ((__uart_lsr = MC_LSR) & MC_LSR_FE)
#define test_parity_error(p)            (__uart_lsr & MC_LSR_PE)
#define test_overrun_error(p)           (__uart_lsr & MC_LSR_OE)
#define test_break_error(p)             (__uart_lsr & MC_LSR_BI)
#define clear_frame_error(p)            /* Cleared by reading LSR */
#define clear_parity_error(p)           /* --//-- */
#define clear_overrun_error(p)          /* --//-- */
#define clear_break_error(p)            /* --//-- */

#define setup_baud_rate(p, khz, baud) {                                 \
                unsigned divisor = MC_DL_BAUD (khz * 1000, baud);       \
                MC_LCR = MC_LCR_8BITS | MC_LCR_DLAB;                    \
                MC_DLM = divisor >> 8;                                  \
                MC_DLL = divisor;                                       \
                MC_LCR = MC_LCR_8BITS;                                  \
        }

static unsigned __uart_lsr;

15. Четвёртый тест: асинхронный порт

В каталоге uos/examples/mips-mc24 создаём файл test_uart.c:

#include <runtime/lib.h>
#include "kernel/uos.h"
#include "uart/uart.h"

ARRAY (task, 0x400);
uart_t uart;

void hello (void *data)
{
        for (;;) {
                puts (&uart, "\nHello, World! ");
                getchar (&uart);
        }
}

void uos_init (void)
{
        debug_puts ("\nTesting UART.\n");
        uart_init (&uart, 0, 90, KHZ, 115200);
        task_create (hello, 0, "hello", 1, task, sizeof (task));
}

В файле Makefile устанавливаем TESTS = test_uart.hex. Запускаем "make". В результате получаем файл, который загружаем в ПЗУ целевой платы. При включении на консоль должна выдаваться строка "Hello, World".

16. Комплексный тест: пять обедающих философов

Проведём комплексную проверку микроядра, драйвера таймера и драйвера асинхронного порта. В каталоге uos/examples/mips-mc24 создаём файл examples/mips-mc24/philosophers.c. В нем помещаем код решения классической задачи пяти обедающих философов.

В файле Makefile устанавливаем TESTS = test_philosophers.hex. Запускаем "make". В результате получаем файл, который загружаем в ПЗУ целевой платы. При включении на консоли будут отображаться пять философов и изменение их состояния во времени.

Перенос системы uOS на процессор Элвис Мультикор MC-24 можно считать завершённым.

You can’t perform that action at this time.