Machines virtuelles et microcontrôleurs

Lors du développement de différents appareils, vous rencontrez souvent un problème: l'algorithme d'un appareil à l'autre est répété par endroits et les appareils eux-mêmes sont complètement différents. J'ai trois appareils en cours de développement qui, à certains endroits, répètent les fonctionnalités les uns des autres, ils utilisent trois processeurs différents (trois architectures différentes), mais il n'y a qu'un seul algorithme. Afin de tout unifier en quelque sorte, il était prévu d'écrire une machine virtuelle minimale.



En général, je regardais vers le bytecode de Java, Lua et d'autres machines, mais je ne voulais pas réécrire tous les bagages disponibles dans une autre langue. Nous avons donc décidé de la langue - C. Bien que Java ou Lua semble toujours attrayant. [1] [2] [3] [4].

Le critère suivant était le compilateur. Dans mes projets, j'utilise le plus souvent «écrit par des étudiants pour les cookies GCC (c) anonymus». Ceux. si vous décrivez votre architecture, vous devrez trouver tout le tas de GCC (compilateur, éditeur de liens, etc.).

Comme je suis paresseux, je cherchais la plus petite architecture possible avec le support GCC. Et c'est devenu le MSP430.

Brève description


MSP430 est une architecture très simple. Il n'a que 27 instructions [5] et presque tous les adressages.

La construction de la machine virtuelle a commencé avec le contexte du processeur. Le contexte du processeur dans les systèmes d'exploitation est une structure qui décrit entièrement l'état du processeur. Et l'état de ce processeur virtuel est décrit comme suit:

  • Équipe actuelle
  • Registres
  • État facultatif des registres d'interruption
  • Contenu optionnel de RAM et ROM

Les registres du MSP430 sont 16. Sur les 16 registres, les 4 premiers sont utilisés comme registres système. Disons, un registre nul est responsable du pointeur actuel vers la commande en cours d'exécution à partir de l'espace d'adressage (compteur de commandes).

Vous pouvez en savoir plus sur les registres dans le guide de l'utilisateur d'origine msp430x1xxx [6]. En plus des registres, il y a aussi le contenu de l'espace d'adressage - RAM, ROM. Mais comme il est facile de conserver la «machine hôte» (la machine exécutant le code de la machine virtuelle) dans la mémoire de la machine virtuelle, pour de fréquents, cela ne sert à rien - le rappel est utilisé.

Cette solution vous permet d'exécuter des programmes «complètement gauchers» sur des processeurs avec l'architecture Harvard (lire AVR [7] [8]), en prenant le programme à partir de sources externes (disons, mémoire i2c ou carte SD).

Dans le contexte du processeur également, une description des registres d'interruption (SFR). Le système d'interruption MSP430 est décrit avec la plus grande précision dans [6], clause 2.2.
Mais dans la machine virtuelle décrite, je me suis légèrement éloigné de l'original. Dans le processeur d'origine, les drapeaux d'interruption se trouvent dans les registres périphériques. Dans ce cas, les interruptions sont décrites dans les registres SFR.

La périphérie du processeur est décrite de la même manière, via callback, ce qui vous permet de créer vos propres périphériques à volonté.

L'élément de processeur suivant est le multiplexeur de commandes. Le multiplexeur de commandes remplit une fonction distincte. Le multiplexeur sélectionne la commande elle-même à partir du mot de commande, en adressant la source et le récepteur, et exécute l'action de la commande sélectionnée.

Des fonctions distinctes décrivent l'adressage source (SRC) et le récepteur.

Comment l'utiliser


Dans le dossier d'exemples du référentiel de projet [9], il existe des exemples pour les processeurs suivants:
  • STM8 pour le compilateur IAR
  • STM8 pour compilateur SDCC
  • STM32 pour le compilateur Keil Armcc
  • AVR pour GCC Compiler


Dans le fichier Cpu.h, le processeur est configuré.

Description des paramètres ci-dessous:

  • RAM_USE_CALLBACKS - Indique s'il faut utiliser des appels (rappels) au lieu de tableaux individuels dans le contexte du processeur. Utiliser ou non les appels pour travailler avec la RAM (appelle cpu.ram_read, cpu.ram_write)
  • ROM_USE_CALLBACKS - Indique s'il faut utiliser les appels pour travailler avec la ROM (appelez cpu.rom_read)
  • IO_USE_CALLBACKS - Indique s'il faut utiliser les appels pour travailler avec la périphérie (appelle cpu.io_read, cpu.io_write), si 0, alors les fonctions pour travailler avec la périphérie doivent être décrites dans la fonction msp430_io du fichier cpu.c
  • RAM_SIZE - Taille RAM (RAM), l'adresse de fin est automatiquement recalculée en fonction de ce paramètre
  • ROM_SIZE - Taille de la ROM (ROM), l'adresse de départ est automatiquement recalculée en fonction de ce paramètre
  • IRQ_USE - Indique si les interruptions seront utilisées; si 1, alors les interruptions sont activées
  • HOST_ENDIANESS - Indique l'ordre des octets du contrôleur hôte (le contrôleur qui exécute la machine virtuelle). Les architectures AVR, X86, STM32 sont petits-boutiens, STM8 sont gros-boutiens
  • DEBUG_ON - Indique si le débogage sera utilisé. Le débogage se fait via fprintf - stderr


L'utilisation de la bibliothèque commence par la connexion de cpu.c et cpu.h au projet.

#include "cpu.h"

Vient ensuite l'annonce du contexte du processeur. Selon l'utilisation des paramètres * _USE_CALLBACKS, le code de déclaration de contexte changera.

pour tous * _USE_CALLBACKS = 1, les déclarations de contexte du processeur ressembleront à ceci:

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
    };


Où les variables * _cb acceptent les pointeurs de fonction (voir les exemples).

Inversement, pour * _USE_CALLBACKS = 0, les déclarations ressembleront à ceci:

msp430_context_t cpu_context =
    {
         .rom = { /* hex program */ },
    };

Vient ensuite l'initialisation du contexte via la fonction:

msp430_init(&cpu_context);

Et exécuter une instruction à la fois via une fonction:

while(1)
    msp430_cpu(&cpu_context);

Les rappels pour travailler avec l'espace d'adressage ressemblent à ceci:

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);

Les adresses d'E / S sont transmises par rapport à l'espace d'adressage 0 (c'est-à-dire si le programme de machine virtuelle accède à P1IN, qui est affecté à l'adresse 0x20, alors l'adresse 0x20 sera transmise à la fonction).

Au contraire, les adresses pour la RAM et la ROM sont transmises par rapport aux points de départ (par exemple, lors de l'accès à l'adresse 0xfc06 et du démarrage de la ROM à 0xfc00, l'adresse 0x0006 sera transmise à la fonction. Autrement dit, l'adresse est de 0 à RAM_SIZE, 0 - ROM_SIZE)

Cela permet l'utilisation de la mémoire externe , par exemple I2C (qui ralentit déjà le processeur).

Comment compléter


Complètement le projet n'est pas terminé. Cela fonctionne, le firmware de test fonctionne avec un bang. Mais la plupart des compilateurs n'utilisent pratiquement pas de commandes spécifiques différentes (par exemple, Dadd est l'addition décimale de la source et du récepteur (avec césure)). Il n'est donc pas nécessaire de parler de compatibilité à 100% avec de vrais processeurs.

Naturellement, il y a environ deux douzaines d'opérations de la machine hôte pour une commande de machine virtuelle, il est donc inutile de parler des caractéristiques de vitesse.

Les sources du projet et une description plus détaillée sont disponibles sur bitbucket.org [9].

Je serais heureux si ce projet est utile à quelqu'un.

[1] dmitry.gr/index.php?r=05.Projects&proj=12.%20uJ%20-%20a%20micro%20JVM
[2] www.harbaum.org/till/nanovm/index.shtml
[3]www.eluaproject.net
[4] code.google.com/p/picoc
[5] en.wikipedia.org/wiki/MSP430
[6] www.ti.com/lit/ug/slau049f/slau049f.pdf
[7] en.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] en .wikipedia.org / wiki / AVR
[9] bitbucket.org/intl/msp430_vm

Source: https://habr.com/ru/post/fr385009/


All Articles