Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ncfavier/marvin

Microprocessor simulator for the digital electronics project at ENS
https://github.com/ncfavier/marvin

digital-circuits microprocessor simulator

Last synced: 2 days ago
JSON representation

Microprocessor simulator for the digital electronics project at ENS

Awesome Lists containing this project

README

        

# marvin

Ce projet est réalisé en Haskell (GHC 8.6.5), et dépend des bibliothèques `base`, `containers`, `array`, `mtl`, `megaparsec` et `time`.

Il contient les modules suivants :

- `Netlist` : définition et parsage des netlists ;
- `Simulator` : simulateur de netlists ;
- `Dialog` (exécutable `dialog`) : interface « dialogue » du simulateur : lit les variables d'entrée au clavier et affiche les variables de sortie ;
- `Clock` (exécutable `clock`) : interface « horloge » du simulateur : ne lit aucune variable, affiche l'heure et la date dans un format lisible, gère l'initialisation et la synchronisation ;
- `Assembler` (exécutable `assembler`) : compile un programme assembleur vers une image RAM chargeable par le simulateur.

## Simulateur

dialog [-n STEPS] NETLIST

La ROM et la RAM sont représentées comme des tableaux de booléens, afin de ne pas privilégier une taille de mot donnée.

Les entiers sont interprétés et stockés de manière gros-boutiste.

On maintient un environnement associant une valeur à chaque variable de la netlist (initialement zéro).

La netlist n'est pas supposée triée : si une variable `a` dépend d'une variable `b`, on calculera `b` avant `a`. À chaque étape, on calcule uniquement les variables suivantes (et leurs dépendances) :

- les variables d'entrée ;
- les variables de sortie ;
- les variables `x` apparaissant dans une équation de la forme `... = REG x` ;
- les variables `x` apparaissant dans une équation de la forme `x = RAM ...`.

Cela permet de ne pas calculer les variables non utilisées, et je conjecture que c'est suffisant pour effectuer tous les effets de bord.

La RAM (resp. la ROM) est initialisée avec le contenu de l'image `ram.img` (resp. `rom.img`) si elle existe, et complétée par des zéros.

Le format pour les images RAM/ROM est le suivant :

- un entier par ligne ;
- le premier entier est la taille de mot `n` ;
- le deuxième entier est la taille `m` de l'image en mots ;
- le reste interprété comme une suite de mots de `n` bits, concaténés et éventuellement complétés pour atteindre `m` mots.

## Microprocesseur

Le microprocesseur manipule des mots de **42 bits**. Il comporte une ALU, une RAM, une ROM, ainsi que les registres `A`, `B`, `C`, `D` et `PC` (compteur d'instruction).

Chaque instruction occupe exactement trois emplacements mémoire : un *opcode* et deux opérandes (optionnelles).

Les différentes parties du microprocesseur sont orchestrées par divers signaux de contrôle, chacun correspondant à un bit de l'opcode en cours d'exécution.

Les instructions suivantes sont disponibles :

- `mov` : déplace une opérande vers l'autre ;
- `add`, `sub` : addition, soustraction ;
- `mul`, `div`, `mod` : multiplication, division, modulo ;
- `not`, `and`, `or`, `xor` : opérations logiques ;
- `test` : met la destination à 0 si la source vaut 0, à 1 sinon ;
- `jmp` : saut ;
- `jz`, `je`, `jnz`, `jne`, `jl`, `jnl`, `jge`, `jg`, `jng`, `jle` : sauts conditionnels par rapport au registre `A`.

Les opérations arithmétiques et logiques prennent une opérande source et une opérande destination, et placent le résultat dans l'opérande destination.

Les opérandes peuvent faire référence à un entier immédiat, un registre, ou un emplacement mémoire ; le type d'opérande est codé sur les deux bits de poids faible. Les deux opérandes ne peuvent pas faire référence à la mémoire en même temps.

Le microprocesseur est écrit en minijazz (`proc.mj`), et compilé vers la netlist `proc.net`.

Il dépend d'une version légèrement modifiée de minijazz (fournie dans le dépôt) : le type des fonctions `reg` et `mux` permet de les utiliser avec des signaux de taille quelconque.

## Assembleur

assembler FILE

L'assembleur permet de compiler depuis un langage assembleur vers une image RAM contenant un programme exécutable par le microprocesseur.

Il supporte trois types d'instructions :

- les affectations : `x = v` ;
- les étiquettes : `label:` ;
- les instructions machine : `ins [op1 [op2]]`.

Les opérandes sont de la forme :

- registre : `%a` ;
- mémoire : `*42`, `*foo` ;
- immédiat : `42`, `foo`.

L'image est écrite sur la sortie standard. La taille de l'image produite est fixée à 2048 mots.

## Horloge

clock [OPTIONS...] NETLIST
-a --async run in async mode
-i TIMEDATE --init=TIMEDATE initial timedate

L'option `-a` permet d'ignorer l'horloge système et de faire tourner l'horloge le plus rapidement possible ; l'option `-i` permet d'initialiser l'horloge à un instant donné (format `%Y-%m-%d %H:%M:%S`).

Certains emplacements en mémoire sont utilisés pour l'entrée-sortie :

- les adresses 1024 - 1029 contiennent les secondes, minutes, heures, jours, mois et années ;
- l'adresse 1030 est mise à 1 en mode synchrone, à 0 sinon ;
- l'adresse 1031 sert à la synchronisation : le simulateur ajoute 1 à cet emplacement pour chaque seconde écoulée.

Le programme assembleur de l'horloge se trouve dans `clock.asm`, et doit être compilé vers `ram.img`. La ROM n'est pas utilisée.

L'horloge tient compte des années bissextiles.

## TL;DR

La cible `runclock` du Makefile fourni permet de tout compiler et de lancer l'horloge en mode synchrone.

La variable `async` peut être définie pour activer le mode asynchrone (`make runclock async=1`).