- PVSM.RU - https://www.pvsm.ru -
Разрабатывая разные устройства, очень часто получаешь проблему: алгоритм от устройства к устройству местами повторяется, а сами устройства полностью разные. У меня три разрабатываемых устройства, которые местами повторяют функционал друг друга, в них используются три разных процессора (три разные архитектуры), но алгоритм один. Чтобы хоть как-то все унифицировать, было задумано написать минимальную виртуальную машину.
В целом, я смотрел в сторону байт-код машин Java, Lua и других, но весь имеющийся багаж особо переписывать на другой язык не хотелось. Так что с языком определились — Си. Хотя Java или Lua все еще заманчиво звучит. [1][2][3][4].
Следующим критерием шел компилятор. Я в своих проектах чаще всего использую «написанный студентами за печеньки GCC (с) анонимус». Т.е. если описывать свою какую-то архитектуру, к ней бы пришлось еще придумывать всю связку из GCC (Компилер, линковщик и т.д.).
Так как я человек ленивый, искал минимально возможную архитектуру с поддержкой GCC. И ею стала MSP430.
MSP430 — очень простая архитектура. Она имеет всего 27 инструкций [5] и практически любую адресацию.
Постройку виртуальной машины начал с контекста процессора. Контекстом процессора в операционных системах называют структуру, которая полностью описывает состояние процессора. А состояние данного виртуального процессора описывается через следующее:
Регистров у MSP430 — 16. Из этих 16 регистров первые 4 используются как системные регистры. Скажем, нулевой регистр отвечает за текущий указатель на выполняемую команду из адресного пространства (Счетчик команд).
Более детально про регистры можно почитать в оригинальном user guide msp430x1xxx [6]. Кроме регистров есть еще содержимое адресного пространства — ОЗУ, ПЗУ. Но так как просто держать в памяти «Хост-машины» (машина, выполняющая код виртуальной машины) память виртуальной машины, за частую, нету смысла — используются callback.
Данное решение позволяет исполнять «совершенно левые» программы на процессорах с гарвардской архитектурой (читай AVR [7][8]), беря программу из внешних источников (Скажем, i2c память или SD карта).
Также в контексте процессора имеется описание регистров прерываний (SFR). Наиболее точно система прерываний MSP430 описана в [6] п. 2.2.
Но в описываемой виртуальной машине я немного отошел от оригинала. В оригинальном процессоре флаги прерываний находятся в регистрах периферии. В данном случае прерывания описывается в SFR регистрах.
Периферия процессора описывается так же, через callback-и, что позволяет создавать свою собственную периферию по желанию.
Следующим пунктом процессора является мультиплексор команд. Мультиплексор команд выполняет отдельная функция. Мультиплексор выбирает из слова команды саму команду, адресацию источника и приемника и выполняет действие выбранной команды.
Отдельными функциями описывается адресация источника (SRC) и приемника.
В папке examples из репозитория проекта [9] есть примеры для следующих процессоров:
В файле Cpu.h выполняется настройка процессора.
Описание настроек ниже:
Использование библиотеки начинается с подключения cpu.c и cpu.h в проект.
#include "cpu.h"
Далее идет обьявление контекста процессора. В зависимости от использования параметров *_USE_CALLBACKS будет меняться код объявления контекста.
для всех *_USE_CALLBACKS = 1 объявления контекста процессора будет выглядеть следующим образом:
msp430_context_t cpu_context =
{
.ram_read_cb = ram_read,
.ram_write_cb = ram_write,
.rom_read_cb = rom_read,
.io_read_cb = io_read,
.io_write_cb = io_write
};
Где переменные *_cb принимают указатели на функции (см. примеры).
Наоборот же, для *_USE_CALLBACKS = 0, объявления будут выглядеть так:
msp430_context_t cpu_context =
{
.rom = { /* hex program */ },
};
Далее идет инициализация контекста через функцию:
msp430_init(&cpu_context);
И выполнение по одной инструкции за раз через функцию:
while(1)
msp430_cpu(&cpu_context);
Callback-и для работы с адресным пространством выглядят следующим образом:
uint16_t io_read(uint16_t address);
void io_write(uint16_t address,uint16_t data);
uint8_t ram_read(uint16_t address);
void ram_write(uint16_t address,uint8_t data);
uint8_t rom_read(uint16_t address);
Адреса для IO передаются относительно 0 адресного пространства (т.е. если в программа виртуальной машины обратится к P1IN, который назначен на адрес 0x20, то и в функцию будет передан адрес 0x20).
Напротив, адреса для RAM и ROM передаются относительно начальных точек (например, при обращение по адресу 0xfc06 и началом ПЗУ по адресу 0xfc00 в функцию будет передан адрес 0x0006. Т.е адрес от 0 до RAM_SIZE, 0 — ROM_SIZE)
Это позволяет использовать внешнюю память, к примеру I2C (что и без того замедляет процессор).
Полностью проект не завершен. Он работает, тестовые прошивки работают на ура. Но большинство компиляторов практически не используют разные специфические команды (скажем, Dadd — десятичное сложение источника и приёмника (с переносом)). Так что говорить о 100% совместимости с реальными процессорами не приходится.
Естественно, на одну команду виртуальной машины приходится с два десятка операций хост-машины, поэтому говорить о каких-либо скоростных характеристиках бессмысленно.
Исходники проекта и более расширенное описание доступно на bitbucket.org [9].
Буду рад, если кому-нибудь пригодится данный проект.
[1] dmitry.gr/index.php?r=05.Projects&proj=12.%20uJ%20-%20a%20micro%20JVM [1]
[2] www.harbaum.org/till/nanovm/index.shtml [2]
[3] www.eluaproject.net [3]
[4] code.google.com/p/picoc [4]
[5] ru.wikipedia.org/wiki/MSP430 [5]
[6] www.ti.com/lit/ug/slau049f/slau049f.pdf [6]
[7] ru.wikipedia.org/wiki/%D0%93%D0%B0%D1%80%D0%B2%D0%B0%D1%80%D0%B4%D1%81%D0%BA%D0%B0%D1%8F_%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D0%B0 [7]
[8] ru.wikipedia.org/wiki/AVR [8]
[9] bitbucket.org/intl/msp430_vm [9]
Автор: intl
Источник [10]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/msp430/100321
Ссылки в тексте:
[1] dmitry.gr/index.php?r=05.Projects&proj=12.%20uJ%20-%20a%20micro%20JVM: http://dmitry.gr/index.php?r=05.Projects&proj=12.%20uJ%20-%20a%20micro%20JVM
[2] www.harbaum.org/till/nanovm/index.shtml: http://www.harbaum.org/till/nanovm/index.shtml
[3] www.eluaproject.net: http://www.eluaproject.net/
[4] code.google.com/p/picoc: https://code.google.com/p/picoc/
[5] ru.wikipedia.org/wiki/MSP430: https://ru.wikipedia.org/wiki/MSP430
[6] www.ti.com/lit/ug/slau049f/slau049f.pdf: http://www.ti.com/lit/ug/slau049f/slau049f.pdf
[7] ru.wikipedia.org/wiki/%D0%93%D0%B0%D1%80%D0%B2%D0%B0%D1%80%D0%B4%D1%81%D0%BA%D0%B0%D1%8F_%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D0%B0: https://ru.wikipedia.org/wiki/%D0%93%D0%B0%D1%80%D0%B2%D0%B0%D1%80%D0%B4%D1%81%D0%BA%D0%B0%D1%8F_%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D0%B0
[8] ru.wikipedia.org/wiki/AVR: https://ru.wikipedia.org/wiki/AVR
[9] bitbucket.org/intl/msp430_vm: https://bitbucket.org/intl/msp430_vm
[10] Источник: http://geektimes.ru/post/263614/
Нажмите здесь для печати.