Auteur Sujet: Un tuto pour créer une VM ou une CPU (from scratch)  (Lu 1119 fois)

bk4nt

  • Full Member
  • ***
  • Messages: 151
    • Voir le profil
Un tuto pour créer une VM ou une CPU (from scratch)
« le: décembre 26, 2024, 10:56:02 pm »
Bonjour,

D'habitude, on appréhende la programmation en faisant clignoter une LED, puis on continue en utilisant des librairies pour exploiter des périphériques (12C, capteur de température, ou servo, etc), sans jamais avoir ni à chercher ni à comprendre comment ça fonctionne. Arduino et Rasperry (etc) ayant ainsi pu permettre à plusieurs générations déjà de faire des choses, parfois facilement, ou même des choses plus compliquées.

C'est plus compliqué lorsqu'un périphérique n'est pas supporté. Il faut alors soi-même créer ou adapter une librairie existante, s'inspirer d'un exemple ou modifier du code.

C'est plus compliqué lorsqu'une librairie a été crée pour un ou pour des modèles précis d'Arduino. Tous n'ont pas les mêmes timers, les mêmes possibilités d'interruptions sur les pin, etc. Parfois, il faut adapter une librairie pour qu'elle puisse fonctionner avec l'Arduino qu'on veut utiliser.

C'est aussi plus compliqué lorsqu'une librairie a un bug ou lorsqu'elle est incomplète. Il faut alors en chercher une autre ou la corriger/compléter.

C'est aussi plus compliqué lorsque du code tel que Free RTOS n'est pas compatible avec la CPU qu'on souhaite utiliser. Il faut un minimum de RAM et de flash pour que ça tienne dans une CPU. Pour une petite CPU, il faut alors "dégraisser" Free RTOS, pour n'en garder que le minimum, afin que le code tienne en RAM/flash de la CPU cible. Ou écrire quelques lignes pour pour créer un RTOS avec des fonctionnalités minimales et utiles au projet.


Ce tuto va beaucoup plus bas, propose de créer une VM, une machine virtuelle, une CPU, en partant de zéro, avec la définition et l'écriture des instructions qu’exécutera la CPU:

https://www.jmeiners.com/lc3-vm/

Des extraits de la présentation:

In this tutorial, I will teach you how to write your own virtual machine (VM) that can run assembly language programs, such as my friend’s 2048 or my Roguelike. If you know how to program, but would like to gain a deeper understanding of what is going on inside a computer and better understand how programming languages work, then this project is for you. Writing your own VM may sound a little scary, but I promise that you will find it to be surprisingly simple and enlightening.

...

The final code is about 250 lines of C (unix, windows). All you need to know is how to read basic C or C++ and how to do binary arithmetic.

...

A VM is a program that acts like a computer. It simulates a CPU along with a few other hardware components, allowing it to perform arithmetic, read and write to memory, and interact with I/O devices, just like a physical computer. Most importantly, it can understand a machine language which you can use to program it.



C'est du très bas niveau, de l'assembleur, mais avec des explications détaillées et du code source simple qui permet de comprendre comment fonctionne une CPU:
while (running)
    {
        /* FETCH */
        uint16_t instr = mem_read(reg[R_PC]++);
        uint16_t op = instr >> 12;

        switch (op)
        {
            case OP_ADD:
                @{ADD}
                break;
            case OP_AND:
                @{AND}
                break;
            case OP_NOT:
                @{NOT}
                break;
            case OP_BR:
                @{BR}
                break;
            case OP_JMP:
                @{JMP}
                break;
            case OP_JSR:
                @{JSR}
                break;
            case OP_LD:
                @{LD}
                break;

Le github est ici: https://github.com/justinmeiners/lc3-vm

Une CPU (codée en 426 lignes) est ici: https://github.com/justinmeiners/lc3-vm/blob/master/docs/src/lc3.c
« Modifié: décembre 27, 2024, 09:15:55 am par bobyAndCo »

bk4nt

  • Full Member
  • ***
  • Messages: 151
    • Voir le profil
Re : Un tuto pour créer une VM ou une CPU (from scratch)
« Réponse #1 le: décembre 27, 2024, 04:04:16 pm »
Bonjour,

Dans la très grande majorité des cas, on a pas à se poser de telles questions, on écrit un code en C/C++, on le compile avec l'IDE et on le téléverse dans un Arduino.

Je constate qu'il existe 9 articles sur l'assembleur sur le site de Locoduino:
https://www.locoduino.org/spip.php?article280

Ce premier article compare un blink.c à un blink\main.asm, introduit également l'assembleur inline (quelques lignes d'assembleur ajoutées dans un programme écrit en C/C++).

Cette intro présentant l'assembleur comme étant plutôt réservé aux experts, et ajoute un moment: "L’idéal est donc d’utiliser le langage C en appelant certaines fonctions écrites en assembleur uniquement lorsque cela présente un avantage (respect d’un timing bien particulier par exemple)."

On peut s'intéresser au fonctionnement d'une CPU par simple curiosité. Mieux comprendre son fonctionnement sera utile dans ces cas particuliers.


L'article de Locoduino précise aussi que "Chaque microcontrôleur possède son assembleur [...]  si vous voulez écrire un programme en assembleur : si vous changez de microcontrôleur, il faudra réécrire le programme au moins en partie, voire en totalité !"

C'est le sujet de la portabilité. Les AVR dont différents des esp32, différents aussi des Teensy ou des STM32 (tous différents aussi de cette CPU virtuelle). Si le besoin est un code avancé avec un timing précis à base de quelques lignes d'assembleur, il faudra adapter les lignes selon la CPU qu'on souhaite utiliser. Là encore, comprendre le fonctionnement d'une CPU (de façon générale) sera utile.


On connaît tous la fonction Delay(), en millisecondes, c'est des premières qu'on découvre avec blink.c. Pour un délais très court, on peut utiliser l'instruction ou OP code "NOP": ne rien faire pendant un cycle d'horloge. Pour un timing précis et court, le code en assembleur inline pourrait parfois être aussi simple que:

asm("nop; nop; nop; nop; nop; nop;");

//Or
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");

Ici, pour un timing simple, on pourrait avoir à adapter le nombre de NOP selon la clock/quartz de la CPU cible.


Dans l'article 4 de Locoduino, en conclusion, je lis: "C’est donc dans les cas de projets très pointus que vous aurez besoin de programmer en assembleur, ou bien tout simplement parce que cela vous plaît et que ça représente un nouveau challenge dans votre connaissance des microcontrôleurs."

https://www.locoduino.org/spip.php?article283


L'article 6 est intitulé "Mélanger assembleur et C/C++" et reste encore orienté timing:

https://www.locoduino.org/spip.php?article287

Un appel d'une fonction écrite en assembleur, depuis du C/C++. Dans leur ensemble, ces premiers articles présentent également  l'assembleur comme étant optimisé en terme d'instructions, pour du code court, optimisé, maitrisé et rapide.


Dans une application basse consommation, pour économiser l'énergie, on pourrait avoir envie d'utiliser de l'assembleur et ce afin de remettre au plus vite la CPU en veille entre deux périodes d'activités. Ce qui rejoint l'intro de l'article 6:

"Après avoir lu les articles précédents, vous êtes maintenant capables de concevoir des routines écrites en assembleur au sein de programmes plus complexes écrits en C/C++. En opérant ainsi, vous exploitez la convivialité et l’efficacité du langage C/C++ tout en lui apportant un outil supplémentaire capable d’offrir un code plus compact, s’exécutant plus rapidement et respectant un timing très précis. ..."

https://www.locoduino.org/spip.php?article289
« Modifié: décembre 27, 2024, 05:23:42 pm par bk4nt »