Projet Wireworld

De Wiki LOGre
Aller à : navigation, rechercher
Langue : Français  • English

Sommaire

Wireworld est un automate cellulaire defini par quelques regles simples mais qui Turing-comple et qui permet de simuler des composants electroniques. Vous pouvez trouver plus de details sur sa Wireworld page wikipedia

Vocabulaire

  • Generation : nombre d iterations de l automate. La premiere generation est la generation 0.
  • Periode : dans le cas de quelque chose qui se repete la periode va definir le nombre de generations necessaires pour revenir au meme etat
  • Technologie n microns : n est la periode entre 2 electrons dans un train d electron

Simulateurs

J ai écrit mes propres simulateurs pour cette automate.

  • Une version dont le moteur est en C++ : Repo
  • Une version dont le moteur est en SystemC : Repo

J utilise la lib SDL 1.2 pour l affichage graphique et xmlParser comme parser XML

Mutualisation

Tout ce qui est indépendant du moteur est regroupe dans un package commun qui comprend :

  • Le parser qui lit le fichier décrivant l automate
  • L analyseur qui va partitionner le design pour déterminer les parties qui n ont pas besoin d être simulées, calculer le voisinage etc
  • Le parser de configuration générique en XML qui permet de rendre un design paramétrable
  • Le parser de configuration qui définit les paramètres du design

Formats de fichier

Fichier d entrée

L automate est décrit par un fichier texte ou chaque caractère représente une cellule:

  • . : cellule vide
  • # : cellule cuivre
  • E : cellule tête d électron
  • Q : cellule queue d électron

Voici un exemple :

......................................................................#.......#.#........................................
......................................................................#.......#.#........................................
......................................................................#........##........................................
......................................................................#.........#........................................
...#################..###.......###.......###.....#.###.......###.....#.###.....#.###.......###.......###.......###......
..E.................###.##.##.###.##.##.###.##.##.###.##.##.###.##.##.###.##.##.###.##.##.###.##.##.###.##.##.###.##.##..
...Q################..#.#.#..#..#.#.#..#..#.#.#..#..#.#.#..#..#.#.#..#..#.#.#..#..#.#.#..#..#.#.#..#..#.#.#..#..#.#.#....
......................###.#.###.###.#.###.###.#.###.###.#.###.###.#.###.###.#.###.###.#.###.###.#.###.###.#.###.###.#....
.......................#..#..#...#..#..#...#..#..#...#..#..#...#..#..#...#..#..#...#..#..#...#..#..#...#..#..#...#..#....
.......................#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#...
.......................#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#.#...#.#.#...
........................#..#.#....#..#.#....#..#.#....#..#.#....#..#.#....#..#.#....#..#.#....#..#.#....#..#.#....#..#.#.
..........................#..#......#..#......#..#......#..#......#..#......#..#......#..#......#..#......#..#......#..#.
..........................#..#......#..#......#..#......#..#......#..#......#..#......#..#......#..#......#..#......#..#.
..........................#..#......#..#......#..#......#..#......#..#......#..#......#..#......#..#......#..#......#..#.
...........................##........##........##........##........##........##........##........##........##........##..

Fichier de configuration générique

Il est au format XML et définit un certains nombres d item associes a des coordonnées de cellules qui seront éventuellement tête et queue d électron.
Les coordonnées définies dans les items sont relatives a celles de l origine définie en début de fichier.
Lorsqu un item est défini comme actif alors les cellules associées sont définies comme étant des têtes et queue d electrons sinon leur état reste celui définit par le fichier d entrée.

<?xml version="1.0" encoding="UTF-8"?>
<generic_definition version ="1.0">
  <origin coord="178,182"/>
  <item_list>
    <item name="init_pulse_1" e_head="-8,11"/>
    <item name="init_pulse_2" e_queue="-15,11"/>
    <item name="shift_1" e_head="-167,13" e_queue="-168,13"/>
    <item name="shift_2" e_head="-173,13" e_queue="-174,12"/>
  </item_list>
</generic_definition>

Fichier de configuration

il complète le fichier de configuration générique en définissant si les items sont actifs ou non.
Son format est très simple : nom_d_item:[0|1]
Voici un exemple :

# Test file
init_pulse_1:1
init_pulse_2:1
shift_1:0
shift_2:1
#EOF

Simulateur SystemC

Chaque cellule de l automate est représentée par un module SystemC comprenant

  • Un port d entre booléen pour le signal d horloge
  • Un tableau de port d entrée booléens dont la taille dépend du nombre de voisins
  • Un port de sortie booléen indiquant si la cellule est une tête d électron

Entrées/Sorties du module SystemC

Le module SystemC comprend un process SystemC sensitif sur la clock qui en fonction de l etat interne de la cellule va:

  • compter le nombre d entrée booléennes a 1
  • mettre a jour l état interne de la cellule
  • mettre a jour la sortie booléenne en fonction de l état interne de la cellule

Les sorties des cellules voisines sont reliées aux entrées de la cellule.
Afin d améliorer les performances le module SystemC est template sur le nombre de cellules voisines et le bon type de module est instancie en fonction du nombre de voisins.

Reverse engineering du Wireworld Computer

Le Wireworld computer est un design base sur l automate cellulaire wireworld. Il définit un processeur de type URISC avec une architecture TTA contenant 64 registres de 16 bits
Ce design a été réalisé entre 1990 et 1992 par David Moore, Mark Owen et d autres personnes et peut être considéré comme un précurseur de ce que certains font aujourd’hui avec Minecraft et son extension Redstone
Une machine de Turing basée sur l automate cellulaire du jeu de la vie avait été conçue plusieurs années avant le Wireworld Computer mais sa programmation etait bien moins "pratique" que celle du wireworld computer
Lorsque j'ai découvert ce design et que je l ai vu simulé j ai été immédiatement fasciné. Le site web explique globalement son fonctionnement mais une grande partie du design n est pas reellement expliquee et cela m a intéressé de comprendre les points suivants:

  • comment le design fonctionne
  • comment la complexité émerge de règles aussi simples
  • comment les auteurs ont réussi a contourner les difficultés posées par cet automate: contraintes de propagation, contraintes spatiales dues a l univers 2D etc

Avant de passer a la suite il est intéressant de lire les quelques pages du site du wireworld computer pour comprendre les principes de fonctionnement du wireworld computer

Wireworld Computer


Methodologie

  • J utilise mon simulateur C++ pour faire tourner le design en mode performance
  • J utilise mon simulateur C++/SystemC pour faire tourner le design en mode debug:
    • L’évolution de l état des différentes cellules de l automate est enregistre en format VCD
    • J utilise GTKwave pour le relire
  • Pour obtenir une représentation en porte logique du design j utilise Logisim. Cela me permet d avoir une vue plus compréhensible du fonctionnement

Système de Clock

Une maniére simple de générer une clock de période P est de dessiner une boucle de taille P contenant un seul electron.
Cette approche fonctionne très bien pour les petites valeurs de P mais devient vite couteuse en espace pour des valeurs plus importantes.
Le système de clock adresse ce problème par un design élégant et compact permettant de générer plusieurs clocks de période relativement importante dans un espace restreint

Système de clock

Il est constitué par

  • Un clock injector
  • Une chaine d'unité de division de clocks

Clock injector

Simple boucle qui injecte un électron dans le générateur d horloge avec une période 36
La cellule de cuivre située la plus a gauche de la boucle est considérée comme origine des temps et origine du repère permettant de situer les autres cellules

Unité de division de clock

Elle permet de diviser la fréquence de clock par deux.
Elle est constituée de 2 portes logiques ( XOR et A & /B ) d'une boucle et d une ligne a retard.
La boucle sert de générateur d électron et a une période de 12.
36 étant un multiple de 12 et la boucle étant alimentée par le clock injector lorsque la boucle sera pleine un de ses electrons arrivera sur la porte XOR en même temps que l électron du clock injector
Un électron met 4 générations pour passer de la cellule commune a la boucle et a la ligne de retard jusqu a l entrée A de la cellule A & /B
Un électron met 16 générations pour passer de la cellule commune a la boucle et a la ligne de retard jusqu a l entrée /B de la cellule A & /B
Le retard introduit est donc de 12 générations soit la période de la boucle ce qui fait que l électron n arrivera sur la porte A & /B en même temps que l électron n - 1. Seul le premier électron arrivera seul sur la porte et pourra donc la passer
Dans le schéma ci dessous la période de la boucle et le retard de la ligne a retard son modélisées par une bascule D.

  • Au démarrage la boucle est vide. Le premier électron qui arrive la remplit. Grâce au retard induit par la ligne a retard le premier électron passe la porte A&/B mais pas les suivants.
  • Le prochain électron qui arrive sur la porte XOR va vider la boucle mais n atteindra pas la sortie a cause du blocage de la porte A& /B et l on est prêt pour un nouveau cycle

De cette maniere seul un electron d entree sur deux attend la sortie

Circuit equivalent a l unite de division de clock

Fonctionnalite realisee

La période du clock injector étant de 36 et chaque boucle divisant la fréquence par 2 ( ie multipliant la période par 2) le système de clock permet de générer dans un design relativement compact plusieurs fréquences de clocks.
Il est compose de 10 unités de division de clock et dipose de sorties après la 5eme boucle et la 6eme boucle ce qui permet d avoir des périodes de sorties respectives de :

  • Apres la boucle 5 : 36 * 2^5 = 36 * 32 = 1152
  • Apres la boucle 6 : 36 * 2^6 = 36 * 64 = 1152 *2 = 2304

Afficheur de chiffre

L affichage des nombres dans le WC est effectue par 5 afficheurs de chiffres
Chaque afficheur de chiffre est compose de:

  • 1 Afficheur 7 segments
  • 1 ROM de 10 entrees et 7 sorties
  • 1 controleur de ROM

Afficheur 7 segment

Afficheur 7 segments

Il s agit de cellules de wireworld assemblées pour représenter un afficheur 7 segments
Chaque segment est alimente en électron par un fil qui se subdivise en plusieurs fils.
Les fils remplis d électrons représentent les segments allumés.
Afin de maximiser la "brillance" d un fil il faut que les électrons soient très rapprochés, c est a dire utiliser la technologie 3 microns qui est la plus fine permise par Wireworld.
Le reste de l afficheur de chiffre fonctionnant en 6 microns chaque entrée de l afficheur 7 segments est munie d un doubleur de fréquence:

  • Chaque électron arrivant ( avec une période 6 ) est dupliqué et retardé de 3 générations avant d être réintroduit dans une une porte OR ce qui génère 2 electrons avec une période de 3
7 segment display

ROM

ROM 10*7

Elle code la correspondance entre un nombre et sa représentation en afficheur 7 segments
Son fonctionnement n est pas le même celui d une ROM classique. En effet dans une ROM classique les entrées codent en binaire l adresse de la ROM que l on veut lire.
Ici une entrée correspond a une adresse, il y a donc autant d entrées que d adresse et seule une entrée doit être active a la fois : l entrée n code pour le nombre n.
Pour un électron arrivant sur l entrée un électron sera généré sur chaque sortie codant 1 dans la ROM.
Pour maintenir éclairés les segments de l afficheur 7 segments il faut donc alimenter l entrée du chiffre correspondant sur la ROM en continue.
Cette fonctionnalité est réalisée par le contrôleur de ROM

Modélisation de la ROM

Dans cet exemple les temps de propagation des electrons n ont aucun rôle fonctionnel donc ils n ont pas été modélisés. La ROM a été implémentée partiellement: seuls les chiffres de 0 a 6 sont gérés

  • Dans un premier temps le circuit est vide, on introduit un électron pour allumer le zero
  • A chaque cycle la colonne active va se décaler ce qui affichera le chiffre suivant
Modélisation de la ROM du contrôleur d affichage de chiffre

Contrôleur de ROM

ROM et son contrôleur

Son rôle est de gérer l alimentation en continu de la ROM et de sélectionner quelle entrée de la ROM va être alimentée en électrons
Il est basé sur une technologie 6 microns

Chaque entrée de la ROM est branchée a une boucle de période 6 qui traverse une porte OR, pour l introduction d un électron dans la boucle, et d une porte A & /B, pour le vidage de la boucle.
Les boucles de chaque entrées sont reliées de la manière suivante :

  • un fil sortant de la boucle n - 1 rentre dans l entrée A d une porte logique A & /B, contrôlant le transfert de boucle, tandis que la sortie de cette porte est reliée a l entrée de la porte OR d introduction d un électron de la boucle n
  • un fil sortant de la boucle n + 1 rentre dans l entrée B de la porte logique A & /B de vidage de la boucle n

De cette façon si la boucle n est alimentée et que l entrée B de porte de transfert de boucle de n vers n+1 est alimentée alors la boucle n reste active.
Si l alimentation de l entrée B est interrompu alors l électron tournant dans la boucle n va être duplique dans la boucle n+1 et va activer le vidage de la boucle n.
Grâce a ce mécanisme il n y a donc qu une boucle active a la fois et par conséquent une seule entrée de la ROM active a la fois.

ROM contrôleur

Les entrées B des portes logiques de transfert de boucles sont toutes reliées a un même fil branché sur la sortie d une porte A & /B dont l entrée A est pilotée par une générateur d électron de période 6.
En contrôlant l entrée B on peut bloquer les électrons émis ce qui coupe l alimentation des portes transfert de boucle.
Il faut 10 générations pour effectuer un transfert de boucle donc pour chaque électron bloqué on décale de 1 l entrée active de la ROM.

Remarques:

  • Pour que le système fonctione il faut que l électron tournant dans la boucle arrive sur l entrée de la porte de contrôle du transfert de boucle en même temps que l électron émis par le générateur
  • Il existe une boucle 10 qui permet de purger la boucle 9
  • La porte logique OR contrôlant l introduction de la boucle 0 est alimentée par un fil d entrée passant a travers une porte logique de type n & !(n+1) pour technologie 6 microns ce qui signifie qui si l on envoie n electrons, seul le n-ieme passera la porte logique.

Fonctionnement

  • Si l on envoie 10 electrons sur ce fil et sur celui du fil de contrôle du transfert de boucle on purge les 10 boucles d entrée de la ROM et on introduit un électron dans la boucle 0 ce qui va afficher un 0 sur l afficheur de 7 segments.
  • Si l on veut afficher 5 et que le chiffre affiche est 0 il faut envoyer 5 electrons sur le fil de contrôle du transfert de boucle pour que la boucle 5 soit activée

Modélisation du contrôleur de ROM

Dans cet exemple les bascules D sont la uniquement pour modéliser les délais des electrons
Seuls les boucles de contrôle des chiffres 0 a 3 + la boucle de vidage ont été modélisées
Pour conserver un modèle de taille raisonnable j utilise une vraie ROM adressable ou seules les adresses ayant un seul bit a 1 sont utilisées

  • Dans un premier temps le circuit est vide, on introduit un électron pour allumer le zéro
  • Au bout de quelques cycles on envoie une salve de 3 électrons sur l entrée B pour faire afficher 3
  • Au bout de quelques cycles on envoie une salve d électron pour purger les boucles et reseter l affichage
Modélisation du contrôleur de ROM

Data latch

Ce module contrôle :

  • quand la donnée provenant du registre 0 est prise en compte et envoyee vers le convertisseur binaire/BCD
  • Le reset des afficheurs de chiffre

Entrées/sorties

Il possède 3 entrées:

  • Donnée provenant du registre 0 ( en bas a gauche de l image)
  • Commande Write indiquant qu'une nouvelle valeur a été écrite dans le registre 0 ( en bas a droite de l image)
  • Commande Set pour faire charger la data dans l additionneur du convertisseur binary/BCD ( en haut a droite de l image)

La commande Set provient de la mega boucle inférieure du convertisseur Binaire/BCD

Il possède 2 sorties:

  • Reset des afficheurs qui va remettre tous les afficheurs a zéro ( en haut a gauche de l image)
  • Données a charger dans l additionneur (en haut au centre de l image)

Architecture interne

Data Latch

Il est constitué des composants internes suivants:

  • Clock de 3 microns toujours active (C1) + 1 transistor (Tf)
  • Clock de 6 microns avec set et reset (C2)
  • Clock de 6 microns toujours active (C3)
  • 3 transistors (T1,T2,T3)
  • 3 portes XOR pour implémenter un croisement de piste
  • 1 chemin pouvant contenir 16 electrons en 6-microns
  • 1 "header" de burst 6 microns qui ne laisse passer que le 1er électron d un burst

Fonctionnement

La mega boucle inférieure du convertisseur Binaire/BCD, reliée a l entrée Set du Data Latch contient un électron de commande Set ainsi qu une suite de valeurs arithmétiques en 6 microns.
Les valeurs ne doivent pas générer de commande Set elles doivent donc être filtrées
Ce filtrage est effectue par le biais de la clock 3 microns C1 qui pilote un transistor Tf laissant passer ou pas les electrons provenant de la mega-boucle.
Les valeurs arithmétiques de la boucle sont en 6 microns et en phase avec l horloge 3 microns ce qui signifie que chaque électron des valeurs arithmétiques arrive sur le transistor Tf au moment ou l électron généré par la clock C1 désactive le transistor Tf, les electrons de la mega-boucle ne traversent donc pas le transistor
L électron de commande est légèrement déphasé par rapport a la clock C1 ce qui lui permet de passer le transistor Tf et générer la commande Set.

La commande Set permet d introduire un électron dans la boucle de la clock C2 qui va générer un flot d électron de période 6 microns.
Cette boucle est vidée par la commande Write.
Cette commande est constituée d une suite de 16 electrons en 6 microns mais seul le premier a un réel effet, les suivant videront une boucle déjà vide.
Le flot d électron généré par la clock C2 se répand dans deux pistes differentes :

  • une piste arrivant sur l entrée du transistor T1 puis sur l entrée de contrôle du transistor T2
  • Le chemin central d une capacité de 16 electrons 6 microns et qui arrive sur l entrée de contrôle du transistor T1

Le chemin central croise la piste d entrée Data via les 3 portes Xor et rentre aussi dans un filtre qui va laisser passer uniquement le 1er électron arrivant de la boucle pour qu il aille dans la sortie de Reset des afficheurs

Quand le chemin central est plein ses electrons désactivent en permanence le transistor T1 qui ne laisse plus passer les electrons venant de la clock C2.
Ces electrons désactivent le transistor T2 qui empêche les electrons de la clock C3 d atteindre le transistor T3
Le transistor T3 contrôle le passage de l entrée Data vers la sortie Data

  • En fonctionnement normal:
    • La clock C2 est active et la boucle centrale remplie
    • Le transistor T1 est donc bloque ce qui empêche les electrons de C2 d atteindre l entrée de contrôle du transistor T2
    • Le transistor T2 est donc passant ce qui permet aux electrons émis par la clock C3 d atteindre l entrée de contrôle du transistor T3
    • Le transistor T3 est donc bloque et empêche la Data d entrée de passer en Data sortie.
  • Lorsque la commande Write arrive:
    • La boucle de la clock C2 est vidée. Il n y a donc plus d electrons émis vers le chemin central
    • Le chemin central se vide
    • T1 devient donc passant mais C2 étant vide il n y a pas d electrons qui passent donc T2 reste passant
    • Le transistor T3 reste donc bloqué et empêche la Data d entrée de passer en Data sortie.
  • Lorsque la commande Set arrive:
    • Un électron est introduit dans la boucle C2 qui se remet a émettre des electrons tous les 6 microns
    • Le chemin central se remplit petit a petit
    • Tant que le chemin central n est pas plein T1 est passant donc les 16 1er electrons de C2 atteignent l entrée de contrôle de T2
    • T2 est bloque par 16 electrons consécutifs donc 16 electrons de l horloge C3 n atteignent pas l entrée de contrôle de T3
    • T3 laisse donc passer les 16 electrons de l entrée Data vers la sortie Data

Une fois le chemin central rempli on repasse en fonctionnement normal.

Remarque : Pour que le data latch fonctionne correctement il faut les contraintes suivantes de timing soient respectées:

  • La Commande Write doit arriver en phase pour vider la boucle de C2
  • La Commande Set doit arriver au bon timing pour que les 16 electrons de la donnée arrivent sur T3 au moment ou celui ci est ouvert

Convertisseur Binaire vers BCD

Pour rappel le BCD est une manière de coder la représentation décimale des nombres en binaire en utilisant 4 bits pour représenter chaque chiffre
Par exemple en BCD 125 se représente 0001 0010 0101.

Le convertisseur binaire/BCD est l une des parties les plus complexes du WC, cela se traduit notamment par le nombre de cellules et la surface qu il occupe.

BCD converter

Entrees/sorties

Le convertisseur binaire/BCD possède 1 entrées et 5 sorties
En entrée il reçoit la donnée binaire sur 16 bits LSB first Les 5 sorties sont:

  • une paire de fil pour chacun des 5 afficheurs de chiffre

Architecture interne

Le convertisseur binaire/BCD est divise en plusieurs parties:

  • Additionneur binaire
  • Sélectionneur de Chiffre
  • Boucle de détection d overflow + générateur de pulse
  • Contrôleur de pulse pour l affichage des chiffres
  • 2 mega boucles

Additionneur binaire

Il s agit d un additionneur série dont la sortie reboucle sur l entrée.
La période de la boucle est de 193 générations ce qui lui permet d opérer sur des chiffres de 32 bits en 6 microns.

Sélectionneur de Chiffre

Entrées/Sorties

Il possède deux entrées:

  • Une entrée reset pilotée par la sortie reset des afficheurs de chiffre du Data Latch
  • Une entrée changement de chiffre pilotée par le contrôleur de pulse pour l affichage des chiffres
  • Une entrée donnée qui reçoit les données envoyées par le générateur de pulse a destination des afficheurs de chiffre

et 5 sorties:

  • Une sortie par afficheur de chiffre
Architecture interne
Selectionneur de chiffre

Le principe de fonctionnement de sa partie contrôle est très similaire a celui du contrôleur de ROM des afficheurs de chiffre

  • Il est constitue d une série de 5 boucles de générations d electrons avec un set et un reset et qui sont chainées entre elles
  • L afficheur[n] est contrôle par la boucle[n], l afficheur[0] contrôle le chiffre des unités
  • La boucle[n+1] contrôle le reset de la boucle[n] de manière a ce que si la boucle[n+1] est active alors la commande reset de la boucle[n] est interceptée
  • Tous les set sont pilotes par l entrée de changement de chiffre

Les sorties des boucles sont branchées sur des transistors qui pilotent la réplication de l entrée B de doubles portes logiques AND NOT sur l entrée A
Ces portes logiques possèdent deux sorties S1 et S2 reliées respectivement a l entrée de contrôle de l afficheur de chiffre et a l entrée B de la porte suivante. Les sorties implémentent les équations suivantes:

  • S1 = /A && B
  • S2 = B && /A

La porte[0] a son entrée B reliée a la sortie du générateur de pulse tandis que les portes[n] ont leur entrée B reliée la sortie S2 de la porte[n-1]
De cette façon lorsque la boucle[n] est active elle bloque le transistor[n] donc la porte[n] a son entrée A a 0 donc S2 = B donc les electrons provenant du générateur de pulse passent a la porte[n+1]
En revanche lorsque la boucle[n] est vide le transistor[n] est passant donc l électron provenant du générateur de pulse est répliqué sur l entrée B.
A cause du délais de propagation A est repassée a 0 donc S1 passe a 1 donc l électron est envoyé vers l afficheur de chiffre

Fonctionnement
  • Par défaut toutes les boucles sont activées
  • Lorsque le Data Latch émet son reset il vide la boucle de l afficheur[4]
  • Lors que le contrôleur de pulse émet une commande de changement de chiffre la boucle de l afficheur actif[n] étant vide le reset de la boucle [n-1] n est pas intercepte donc la boucle[n-1] devient vide a son tour tandis que la commande réactive la boucle[n]
  • Lorsque la boucle[0] est réactivée toutes les autres boucles sont aussi actives donc on revient a l état par défaut

Boucle de détection d overflow + générateur de pulse

Générateur de pulse

Cette boucle a une période de 193 et stocke un électron et est synchronisée avec la boucle de l additionneur binaire
L électron permet de contrôler l activation du set et reset d'une boucle de période 6 ( gen loop ) dans le générateur de pulse:

  • Si il n y a pas d overflow dans l additionneur binaire le reset l emporte sur le set donc pas de pulse généré
  • Si il y a un overflow dans l additionneur le transistor n est plus passant ce qui inhibe le reset donc des pulses sont générés par la boucle de periode 6

Le générateur de pulse produit des séquences de 32 électrons en 6 microns

Contrôleur de pulse

Pulse controlleur

Il possède une entrée:

  • elle reçoit les pulses envoyés par le générateur de pulse

et 2 sorties :

  • sortie de pulse qui émet seulement les pulses nécessaires a l affichage du chiffre
  • changement de chiffre qui va émettre un électron pour indiquer au sélecteur de chiffre de changer de chiffre

Le controleur de pulse est principalement constitue d une boucle de période 769 ce qui lui permet de stocker 4 valeurs de 32 bits en 6 microns
Valeurs stockées dans la boucle du Contrôleur de pulse:

Index Valeur Hexadecimale Valeur Binaire Nombre de bits a zéro
0 0x7FFFFFFF 0111 1111 1111 1111 1111 1111 1111 1111 1
1 0x77777777 0111 0111 0111 0111 0111 0111 0111 0111 8
2 0x7F7F7F7F 0111 1111 0111 1111 0111 1111 0111 1111 4
3 0x7FFF7FFF 0111 1111 1111 1111 0111 1111 1111 1111 2

En plus de ces valeurs la boucle du contrôleur de pulse contient un électron légèrement déphasé par rapport aux autres
Sur la partie supérieure du contrôleur de pulse il y a un transistor Tn piloté par une horloge C3 de période 3 microns qui empêche les electrons des valeurs stockées dans la boucle d être envoyées sur le fil de sortie de changement de chiffre
En revanche l électron déphasé n est pas intercepté et peut donc sortir sur le fil qui pilote le sélecteur de digit ce qui permet de passer d un digit au suivant
Pour bien fonctionner cela nécessite de bien calculer le délais nécessaire pour que les electrons émis par le générateur de pulse pour le digit courant passent par le sélecteur avant le changement de digit !
Les valeurs stockées dans la boucle sont synchronisées avec les salves d electrons émises par le générateur de pulse et contrôlent le transistor Tc situe en bas a droite :

  • Si le bit est a 1 alors Tc n est pas passant et l électron envoyé par le générateur de pulse n atteint pas la sortie.
  • Si le bit est a 0 alors Tc est passant et l électron envoyé par le générateur de pulse atteint la sortie.

Les mega-boucles

Les mégas boucles sont des buffers circulaires de grandes tailles dans lesquels sont stockées des valeurs numériques.
Leur période est de 3841 générations ce qui leur permet de stocker 640 bits en 4 mots soit 20 valeurs de 32 bits en 6 microns
Par rapport au sens de circulation de la boucles les valeurs sont stockées en LSB first

Valeurs stockées dans les mega boucles:

Index Megaloop supérieure Megaloop inférieure
0 0xFFFFFF80 0xFFFF63C0
1 0x0 0x4E20
2 0x0 0x2710
3 0x1890 0x7D0
4 0x0 0xFA0
5 0x0 0x7D0
6 0x0 0x3E8
7 0x3E8 0xC8
8 0x0 0x190
9 0x0 0xC8
10 0x0 0x64
11 0x44 0x14
12 0x0 0x28
13 0x0 0x14
14 0x0 0xA
15 0xA 0x2
16 0x0 0x4
17 0x0 0x2
18 0x0 0x1
19 0x1 0x1

La megaboucle inférieure contient aussi un électron légèrement déphasé par rapport aux electrons codes par les valeurs.
C est cette électron qui ne sera pas filtré par le transistor Tf du data latch et qui sert de commande Set
Le electrons codant les valeurs sont synchronises par rapport a Tf et son donc filtres

Fonctionnement

Principe

Le principe de base de ce convertisseur binaire/BCD est la détection d Overflow
La valeur a afficher va être ajoutée a une série de valeurs prédéfinies stockées dans la megaboucle inférieur ce qui va générer ou non des overflows
Lorsqu un overflow est détecté le générateur de pulse va s activer ce qui va générer des pulses de 32 electrons
Ces electrons arrivent sur l entrée d un transistor piloté par le contrôleur de pulse.
En fonction de la valeur du contrôleur de pulse a cet instant seuls 1,2,4 ou 8 electrons vont passer le transistor et atteindre l entrée du sélecteur de chiffre qui va les envoyer sur le bon afficheur chiffre
Pour afficher un chiffre n il faut donc le décomposer en une somme de 1, 2 4, 8 ce qui revient a le coder en binaire
Comme nous sommes en base 10 pour coder le chiffre[n] il faut le décomposer en une somme de (1, 2 4, 8) * 10 exposant n

Remarque : La salve de 32 electrons générée en cas d overflow est réinjectée dans la carry de l additionneur pour qu il se désactive. Les valeurs contenues dans la megaboucle supérieure sont calculées de manière a ce que la carry ne se propage pas entre deux digits decimaux c est pourquoi on remarque que les valeurs de la megaloop superieure sont non nulles pour les index correspondant au bit de poids fort de chaque chiffre codé en BCD

Arithmétique

Les opérations dans le convertisseur binaire/BCD sont effectuées sur 32 bits, la valeur maximum représentable est donc 0xFFFFFFFF
Si l on soustrait la première valeur de la megaloop inférieure a cette valeur maximum on obtient :

  • 0xFFFFFFFF - 0xFFFF63C0 = 39999

donc tout nombre >= 40000 ajoute a 0xFFFF63C0 générera un overflow. En répétant le processus sur les nombres suivants de la megaloop inferieure on obtient le tableau suivant :

Index Valeur precedente Valeur courante V[n-1] + V[n] Soustraction a Vmax Limite d overflow
0 0xFFFF63C0 0x4E20 0xFFFFB1E0 0xFFFFFFFF – 0xFFFFB1E0 19999
1 0xFFFFB1E0 0x2710 0xFFFFD8F0 0xFFFFFFFF – 0xFFFFD8F0 9999
2 0xFFFFD8F0 0x7D0 0xFFFFE0C0 0xFFFFFFFF – 0xFFFFE0C0 7999
3 0xFFFFE0C0 0xFA0 0xFFFFF060 0xFFFFFFFF – 0xFFFFF060 3999
4 0xFFFFF060 0x7D0 0xFFFFF830 0xFFFFFFFF – 0xFFFFF830 1999
5 0xFFFFF830 0x3E8 0xFFFFFC18 0xFFFFFFFF – 0xFFFFFC18 999
6 0xFFFFFC18 0xC8 0xFFFFFCE0 0xFFFFFFFF – 0xFFFFFCE0 799
7 0xFFFFFCE0 0x190 0xFFFFFE70 0xFFFFFFFF – 0xFFFFFE70 399
8 0xFFFFFE70 0xC8 0xFFFFFF38 0xFFFFFFFF – 0xFFFFFF38 199
9 0xFFFFFF38 0x64 0xFFFFFF9C 0xFFFFFFFF – 0xFFFFFF9C 99
10 0xFFFFFF9C 0x14 0xFFFFFFB0 0xFFFFFFFF – 0xFFFFFFB0 79
11 0xFFFFFFB0 0x28 0xFFFFFFD8 0xFFFFFFFF - 0xFFFFFFD8 39
12 0xFFFFFFD8 0x14 0xFFFFFFEC 0xFFFFFFFF – 0xFFFFFFEC 19
13 0xFFFFFFEC 0xA 0xFFFFFFF6 0xFFFFFFFF – 0xFFFFFFF6 9
14 0xFFFFFFF6 0x2 0xFFFFFFF8 0xFFFFFFFF – 0xFFFFFFF8 7
15 0xFFFFFFF8 0x4 0xFFFFFFFC 0xFFFFFFFF – 0xFFFFFFFC 3
16 0xFFFFFFFC 0x2 0xFFFFFFFE 0xFFFFFFFF – 0xFFFFFFFE 1
17 0xFFFFFFFE 0x1 0xFFFFFFFF
18 0xFFFFFFFF 0x1 0

On voit que les résultats obtenus correspondent bien au codage 1,2,4,8 avec les puissances de dix

Simulation

Le code C++ suivant implemente les principes arithmetiques du convertisseur Binaire/BCD et affiche les etats internes pour avoir une meilleure comprehension de son fonctionnement

#include <iostream>
#include <stdint.h>
#include <stdlib.h>
#include <iomanip>
 
using namespace std;
 
int main(int argc, char ** argv)
{
  if(argc != 2)
    {
      cout << "Usage is binary2bcd <number>" << endl ;
      exit(-1);
    }
  uint64_t l_number = strtoll(argv[1],NULL,0);
  cout << "Input number is " << l_number << endl ;
 
  uint32_t l_bottom_loop[] = {
    0xFFFF63C0,
    0x4E20,
    0x2710,
    0x7D0,
    0xFA0,
    0x7D0,
    0x3E8,
    0xC8,
    0x190,
    0xC8,
    0x64,
    0x14,
    0x28,
    0x14,
    0xA,
    0x2,
    0x4,
    0x2,
    0x1,
    0x1
  };
 
  uint32_t l_top_loop[] = {
    0xFFFFFF80,
    0x0,
    0x0,
    0x1890,
    0x0,
    0x0,
    0x0,
    0x3E8,
    0x0,
    0x0,
    0x0,
    0x44,
    0x0,
    0x0,
    0x0,
    0xA,
    0x0,
    0x0,
    0x0,
    0x1,
  };
 
  uint64_t l_adder_content = l_number;
  uint64_t l_adder_full = 0xFFFFFFFF;
  uint32_t l_display[5] = {0,0,0,0,0};
  uint32_t l_display_index = 0;
  uint32_t l_power_index = 2;
  uint32_t l_carry = 0;
 
  //std::cout << "carry : 0x" << setw(8) << setfill('0') << hex << l_carry << dec << endl ;
  for(uint32_t l_index = 0; l_index < 20 ; ++l_index)
    {
      if(((l_index + 1) % 4) == 0)
	{
	  std::cout << "-------------------------------------------------------";
	  std::cout << "-------------------------------------------------------";
	  std::cout << "-------------------------------------------------------";
	  std::cout << "--------------------------------------------------" << std::endl ;
	}
      std::cout << "Step[" << setfill(' ') << setw(2) << l_index << "]: ";
      cout << "carry : 0x" << setw(8) << setfill('0') << hex << l_carry << dec << " & ~(" ;
      cout << "Top_loop[" << setfill(' ') << setw(2) << l_index << "] : 0x" << setw(8) << setfill('0') << hex << l_top_loop[l_index] << dec << ") => " ; 
      l_carry = l_carry & ( ~ (l_top_loop[l_index]));
      cout << "Adjusted carry 0x" << setw(8) << setfill('0') << hex << l_carry << dec << " ^ " ;
      cout << "Bot_loop[" << setfill(' ') << setw(2) << l_index << "] : 0x" << setw(8) << setfill('0') << hex << l_bottom_loop[l_index] << dec << " => " ; 
      uint32_t l_to_add = l_carry ^ l_bottom_loop[l_index];
      cout << "To add 0x" << setw(8) << setfill('0') << hex << l_to_add <<dec << " + " ;
      cout << "Adder content : " << setw(8) << setfill('0') << hex << l_adder_content << dec << " | " ;
      l_adder_content +=  l_to_add;
      cout << "==> Adder content : " << setw(8) << setfill('0') << hex << l_adder_content << dec ;
      if(l_adder_content > l_adder_full)
	{
	  cout << "\tOverflow !" ;
	  l_adder_content = (l_adder_content & 0xFFFFFFFF) + 1;
	  l_carry = 0xFFFFFFFF;
 
	  // Part implemented by Pulse controler
	  l_display[l_display_index]+= 1 << l_power_index;
	}
      else
	{
	  l_carry = 0;
	}
      // Part implemented by Pulse controler
      l_power_index = (l_power_index > 0 ? l_power_index - 1 : 3);
      // Part implemented by Pulse controler and Digit display controler
      if(l_power_index == 3)
	{
	  ++l_display_index;
	}
      cout << endl;
    }
 
  // Display Results
  for(uint32_t l_index = 0 ; l_index < 5; ++l_index)
    {
      cout << "|" << l_display[l_index] ;
    }
  cout << "|" << endl ;
}

Pour le compiler utilisez la commande suivante

g++ -Wall -ansi -pedantic -g -std=c++11 -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS binary2bcd.cpp -o binary2bcd.exe

Voici un exemple d execution:

$ ./binary2bcd.exe 0x100
Input number is 256
Step[ 0]: carry : 0x00000000 & ~(Top_loop[ 0] : 0xffffff80) => Adjusted carry 0x00000000 ^ Bot_loop[ 0] : 0xffff63c0 => To add 0xffff63c0 + Adder content : 00000100 | ==> Adder content : ffff64c0
Step[ 1]: carry : 0x00000000 & ~(Top_loop[ 1] : 0x00000000) => Adjusted carry 0x00000000 ^ Bot_loop[ 1] : 0x00004e20 => To add 0x00004e20 + Adder content : ffff64c0 | ==> Adder content : ffffb2e0
Step[ 2]: carry : 0x00000000 & ~(Top_loop[ 2] : 0x00000000) => Adjusted carry 0x00000000 ^ Bot_loop[ 2] : 0x00002710 => To add 0x00002710 + Adder content : ffffb2e0 | ==> Adder content : ffffd9f0
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Step[ 3]: carry : 0x00000000 & ~(Top_loop[ 3] : 0x00001890) => Adjusted carry 0x00000000 ^ Bot_loop[ 3] : 0x000007d0 => To add 0x000007d0 + Adder content : ffffd9f0 | ==> Adder content : ffffe1c0
Step[ 4]: carry : 0x00000000 & ~(Top_loop[ 4] : 0x00000000) => Adjusted carry 0x00000000 ^ Bot_loop[ 4] : 0x00000fa0 => To add 0x00000fa0 + Adder content : ffffe1c0 | ==> Adder content : fffff160
Step[ 5]: carry : 0x00000000 & ~(Top_loop[ 5] : 0x00000000) => Adjusted carry 0x00000000 ^ Bot_loop[ 5] : 0x000007d0 => To add 0x000007d0 + Adder content : fffff160 | ==> Adder content : fffff930
Step[ 6]: carry : 0x00000000 & ~(Top_loop[ 6] : 0x00000000) => Adjusted carry 0x00000000 ^ Bot_loop[ 6] : 0x000003e8 => To add 0x000003e8 + Adder content : fffff930 | ==> Adder content : fffffd18
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Step[ 7]: carry : 0x00000000 & ~(Top_loop[ 7] : 0x000003e8) => Adjusted carry 0x00000000 ^ Bot_loop[ 7] : 0x000000c8 => To add 0x000000c8 + Adder content : fffffd18 | ==> Adder content : fffffde0
Step[ 8]: carry : 0x00000000 & ~(Top_loop[ 8] : 0x00000000) => Adjusted carry 0x00000000 ^ Bot_loop[ 8] : 0x00000190 => To add 0x00000190 + Adder content : fffffde0 | ==> Adder content : ffffff70
Step[ 9]: carry : 0x00000000 & ~(Top_loop[ 9] : 0x00000000) => Adjusted carry 0x00000000 ^ Bot_loop[ 9] : 0x000000c8 => To add 0x000000c8 + Adder content : ffffff70 | ==> Adder content : 100000038    Overflow !
Step[10]: carry : 0xffffffff & ~(Top_loop[10] : 0x00000000) => Adjusted carry 0xffffffff ^ Bot_loop[10] : 0x00000064 => To add 0xffffff9b + Adder content : 00000039 | ==> Adder content : ffffffd4
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Step[11]: carry : 0x00000000 & ~(Top_loop[11] : 0x00000044) => Adjusted carry 0x00000000 ^ Bot_loop[11] : 0x00000014 => To add 0x00000014 + Adder content : ffffffd4 | ==> Adder content : ffffffe8
Step[12]: carry : 0x00000000 & ~(Top_loop[12] : 0x00000000) => Adjusted carry 0x00000000 ^ Bot_loop[12] : 0x00000028 => To add 0x00000028 + Adder content : ffffffe8 | ==> Adder content : 100000010    Overflow !
Step[13]: carry : 0xffffffff & ~(Top_loop[13] : 0x00000000) => Adjusted carry 0xffffffff ^ Bot_loop[13] : 0x00000014 => To add 0xffffffeb + Adder content : 00000011 | ==> Adder content : fffffffc
Step[14]: carry : 0x00000000 & ~(Top_loop[14] : 0x00000000) => Adjusted carry 0x00000000 ^ Bot_loop[14] : 0x0000000a => To add 0x0000000a + Adder content : fffffffc | ==> Adder content : 100000006    Overflow !
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Step[15]: carry : 0xffffffff & ~(Top_loop[15] : 0x0000000a) => Adjusted carry 0xfffffff5 ^ Bot_loop[15] : 0x00000002 => To add 0xfffffff7 + Adder content : 00000007 | ==> Adder content : fffffffe
Step[16]: carry : 0x00000000 & ~(Top_loop[16] : 0x00000000) => Adjusted carry 0x00000000 ^ Bot_loop[16] : 0x00000004 => To add 0x00000004 + Adder content : fffffffe | ==> Adder content : 100000002    Overflow !
Step[17]: carry : 0xffffffff & ~(Top_loop[17] : 0x00000000) => Adjusted carry 0xffffffff ^ Bot_loop[17] : 0x00000002 => To add 0xfffffffd + Adder content : 00000003 | ==> Adder content : 100000000    Overflow !
Step[18]: carry : 0xffffffff & ~(Top_loop[18] : 0x00000000) => Adjusted carry 0xffffffff ^ Bot_loop[18] : 0x00000001 => To add 0xfffffffe + Adder content : 00000001 | ==> Adder content : ffffffff
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Step[19]: carry : 0x00000000 & ~(Top_loop[19] : 0x00000001) => Adjusted carry 0x00000000 ^ Bot_loop[19] : 0x00000001 => To add 0x00000001 + Adder content : ffffffff | ==> Adder content : 100000000    Overflow !
|0|0|2|5|6|

Les Overflow representent les bits a 1 du mot code en BCD avec en premier le MSB

Contrôleur d'accès aux registres

Contrôle des registres

Le WC comprend 64 registres, les contrôleurs d accès aux registres permettent de sélectionner les registres a lire ou a écrire
Il y a un contrôleur d accès en écriture et un contrôleur d accès en lecture
La sélection des registres se fait via l envoi de deux salves de 16 electrons en 6 microns, l une partant du bas de la banque de registre en direction du haut et partant du haut de la banque de registre en direction du bas.
Le registre situe au point de rencontre des deux salves devient alors accessible.
La salve partant du haut de la banque de registre est émise avec une période fixe tandis que le moment d émission de la salve du bas vers le haut est contrôlable
En introduisant un délais avant l émission de cette salve on change le point de rencontre et par conséquent le registre accédé.
Le contrôleur d accès aux registre génère les salves avec un délais déterminé en fonction de l Id du registre a accéder.

Entrées/Sorties

Contrôleur de registre en écriture

Le contrôleur d'accès aux registres possède deux entrées et deux sorties.
Les entrées sont les suivantes:

  • Commande d accès qui va activer le contrôleur
  • Donnée contenant l'Id du registre auquel on veut accéder


Les sorties sont les suivantes:

  • Salve de 16 electrons en 6 microns générée avec un délais fixe a partir de la commande d'accès
  • Salve de 16 electrons en 6 microns générée avec un délais variable dépendant du registre Id a partir de la commande d'accès

Architecture interne

Chaque contrôleur d accès est composé des éléments suivants:

  • 2 générateurs de salves de 16 electrons 6 microns : GSup et GSdown
  • 1 générateur de salve de 8 electrons en 6 microns
  • 1 doubleur d électron en 6 microns
  • 2 générateurs de délais en 6 microns
  • 1 header ne laissant passer que le premier electron d un burst

Générateurs de salve de n electrons en 6 microns

Générateur de salve

Ils reçoivent en entrée un électron et génèrent en sortie une salve d électron.
Le principe de fonctionnement est toujours le meme :

  • Un électron active une boucle de période 6 microns
  • Les electrons générés par la boucle vont a la sortie et dans un fil de feedback qui contrôle le vidage de la boucle de période 6 microns.

La longueur du fil de feedback détermine combien d electrons ont le temps d être émis avant que le premier ne vienne vider la boucle.

Doubleur d électron en 6 microns

L électron qui arrive en entrée est envoyé sur 2 fils qui reconvergent sur une porte OU
L un des fils est plus long que l autre de 6 microns ce qui fait qu en sortie on obtient un électron + un électron retarde de 6 microns

Générateurs de délais en 6 microns

Générateur de délais

Ils sont caractérisés par leur période. Le délais généré sera un multiple de leur période qui est elle même un multiple de 6 microns.
n est le facteur ( période / 6)
En entrée ils reçoivent:

  • une valeur V codé sur n bits
  • une salve de n électrons soit une valeur Vbis avec n bits a 1

En sortie ils génèrent:

  • un électron de référence a un temps t
  • un électron a un temps t` tel que t` = t + constante + V * période

Le coeur des générateurs de délais est un soustracteur travaillant sur des valeurs de n bits.
Il est constitue de:

  • deux boucles: Ba la boucle d activation et Bc la boucle de calcul. Elles sont de même période que le générateur et peuvent donc stocker n bits
  • d une porte XOR
  • deux transistor T1 et T2
  • d une boucle B de période 6 contrôlable (set/reset) ce qui permet de stocker un bit

L électron d activation est inséré dans Ba qui contrôle le set de la B.
Le résultat de V AND Vbis est inséré dans la boucle de calcul a l activation du générateur de délais.
Bc passe par la porte XOR dont l autre entrée est la valeur contenue dans B.
Le reset de B est directement lie a Bc donc le premier bit a 1 dans la boucle de calcul vide B
Ba est aussi reliée a un fil en direction de la sortie avec un duplicateur vers un transistor T1 ce qui fait qu il se neutralise lui même sauf si B contient un électron auquel cas l électron d activation dupliqué n atteint pas l entrée inhibitrice du transitor T1.
Lorsque le soustracteur contient la valeur zéro, il n y a aucun bit a 1 pour vider B donc l électron d activation va bien atteindre la sortie puis vider Ba via le transistor T2. La sortie est donc générée par l underflow du soustracteur

Décrit de manière algorithmique on obtient le code suivant pour n=8:

// this code start to be executed at activation
bool B = true;
bool Bc[8]
do
{
  for(int I = 0 ; i < 8 ; ++i)
  {
    bool output_bit = B ^ Bc[I];
    if(Bc[I] == true)
    {
      B = false;
    }
    B[I] = output_bit
  }
} while(!B)
// Send electron to output

Si l on applique ce code a une valeur numérique telle que 0011011 (LSB first) soit 108

Index 0 1 2 3 4 5 6
Bc[Index] en entrée 0 0 1 1 0 1 1
Valeur de B 1 1 1 0 0 0 0
Bc[Index] en sortie 1 1 0 1 0 1 1

Soit une valeur de sortie de 107 ce qui était attendu

Principe de fonctionnement

On a vu que le contrôleur de registre contient deux générateurs de délais:

  • L un ayant une période de 48 et donc base sur un soustracteur 8 bits : Gd48
  • L un ayant une période de 12 et donc base sur un soustracteur 2 bits : Gd12

Les deux générateurs sont chaines de façon a ce que la sortie du générateur de période 48 commande l activation du générateur de période 12
L électron de commande du contrôleur de registre rentre dans le doubleur d électron, ces deux electrons sont envoyés:

  • sur l entrée Vbis de Gd12 en meme temps que les deux electrons LSB du registre Id arrivent sur son entrée V
  • en entrée du générateur de salve de 8 electrons

La salve de 8 electrons est envoyée sur l entrée Vbis de Gd48 en meme temps que les 6 electrons de poids fort de Registre Id arrivent sur entrée V
Le premier des 8 electrons est aussi envoyé sur la commande de GSdown tandis que la sortie de Gd12 commande GSsup

L utilisation des deux générateurs de délais de périodes différentes permet donc de générer de grands délais (Gd48) avec une granularité fine de 12 générations (Gd12) ce qui au final donne le délais d entre l activation de GSup et GSdown est donc

d = 48 * (RegisterId[7:2] >> 2) + 12 * RegisterId[1:0]

aux constantes de propagation pres

Unite de controle

Principe de fonctionnement

Il s agit de l unite qui orchestre l execution du WC en gerant le cycle d instruction:

  • Fetch(1/2) : Lecture du Program Counter dans le Registre[63]
  • Fetch(2/2) : Lecture de l instruction MOV Rs Rt contenue dans le registre[Program Counter]
  • Decode(1/2) : copie la valeur Rs sur l entree Registre Id du controleur d acces aux registres en lecture
  • Decode(2/2) : copie la valeur Rt sur l entree Registre Id du controleur d acces aux registres en ecriture
  • Execute(1/2) : Lecture de la valeur V contenue dans le Registre[Rs]
  • Execute(2/2) : Ecriture de la valeur V dans le Registre[Rt]
  • Incrementation du Program Counter pour passer a l instruction suivante

Afin d ameliorer la vitesse d execution du WC les deux etapes des Fetch/Decode/Execute sont effectuees en paralelle
Par exemple l etape 1 du Fetch se fait via un fil dedie ce qui permet d effectuer le Execute(1/2) pendant ce temps
On voit donc au final qu il faut faire 2 operations de Lecture pour une Operation d ecriture
Cela se traduit dans le design par un fil en sortie de la 5eme boucle du generateur d horloge qui commande le controleur d acces aux registres en lecture et un fil en sortie de la 6 eme boucle du generateur d horloge qui commande le controleur d acces aux registres en ecriture

Le tableau suivant resume l ordonnancement des differentes operations:

Operation 1 Operation 2 Operation 3 Registre Id en Lecture Registre Id en Ecriture Donnee Lue Donnee a Ecrire
Fetch(1/2) Execute(1/2) Decode(2/2) Valeur du PC Resultat du Fetch(2/2) precedent = Rt V = Contenu Registre Rs
Fetch(2/2) Execute(2/2) Decode(1/2) Rs Instruction pointee par le PC V

Implémentation

Unité de contrôle

Entrées/sorties

L unité de contrôle dispose des entrees suivantes:

  • Donnée D provenant de la banque de registre
  • Valeur du Program Counter ou PC
  • Commande lecture Cr
  • Électron double du Contrôleur d accès aux registres en lecture
  • Commande écriture/Commande de lecture du PC

L unité de contrôle dispose des sorties suivantes:

  • Registre Id en lecture
  • Registre Id en écriture/ Donnée a écrire

Architecture interne

L unité de contrôle est constituée de éléments suivants:

  • Incrementeur
  • Porte XOR
  • Double porte A AND NOT B L1
  • Porte OR
  • Transistor Tpc
  • Triple porte XOR
  • Transistor Tr
  • Transistor Tw

Fonctionnement

L électron de la commande de lecture démarre un générateur de salve dont l arrêt est commande par les electrons generes par le doubleur du controleur d access aux registres en lecture ce qui génère au final une salve de 16 electrons en 6 microns
Les electrons de la salve arrivent sur l entrée des transistors Tr et Tw.
Si les transitors ne sont pas pilotés les électrons de la salve sont envoyés:

  • Par Tr sur l entrée Registre Id du contrôleur d’accès aux registres en lecture
  • Par Tw sur l entrée Registre Id du contrôleur d’accès aux registres en écriture et sur le bus Data des registres en écriture.

Le bus Data suit un chemin très long de manière a arriver sur le registre en meme temps que Registre Id d ecriture est defini par Decode(2/2)'.
Lors du Decode(1/2) la valeur n est pas une instruction donc les timings sont calcules pour quel arrive sur le Registre Id entre deux commandes d’écriture donc elle est ignorée. On a vu que Tr et Tw laissent passer les electrons de la salve lorsqu ils ne sont pas contrôlés ce qui signifie que Sortie = NOT entrée
Lors de la lecture des registres on obtient en sortie NOT du contenu du registre et cette sortie est redirigée vers l entree de Tw donc en sortie de Tw on obtient bien la valeur contenue dans le Registre.
L operation de lecture peut etre soit une lecture de registre soit une lecture de PC pour le fetch d instruction, il y a donc un peu de logique pour gerer les deux cas.
L electron de la commande d écriture est aussi envoyé en direction du registre PC
Il commande un generateur de salve de 8 electrons en 6 microns et l incrementeur

Sélecteur PC/Data registre
Sélecteur PC/Data registre

La salve est envoyee en entree d une porte XOR dont l autre entrée recoit la valeur du PC.
En sortie de la porte XOR on obtient donc NOT(PC) qui arrive sur la double porte A AND NOT B L1 dont l autre entree recoit le PC. Les deux entrées n’étant jamais a 1 en meme temps la porte se comporte comme un croisement de fil et donne sur sa sortie inferieure PC et sur la sortie superieure NOT(PC).
Le NOT(PC) arrive en entrée d'une porte OR avec la donnée provenant des registres.
la Sortie de la porte OR arrive sur le transistor Tpc piloté par la valeur du PC.

Dans le cas ou on a simplement une lecture de registre la porte XOR a une entrée a 0 donc en sortie elle donne PC.
La porte L1 fait un AND NOT de PC et PC donc elle sort rien, la porte OR donne donc en sortie la valeur venant des registres et le transistor n etant pas pilote la valeur part en direction de Registre Id en lecture.

En mode lecture du PC la porte OU recoit en entrée la valeur provenant des registres et NOT(PC).
Le transistor est piloté par PC donc en sortie du transistor on obtient ((NOT(DATA)) OR (NOT(PC))) & NOT(PC).

  • Lorsque le bit du PC est a 1 la sortie est forcement zero
  • Lorsque le bit du PC est a zero la sorties est NOT(Data) OR NOT(PC), comme le bit du PC est a zero le OR donne un 1.

La sortie du transistor est donc NOT(PC)

Incrémenteur de PC
PC incrementeur

La valeur du PC traverse la porte XOR de l incrémenteur dont l autre entrée provient d une boucle 6 micron pilotée Linc. La boucle est chargée par l électron de commande de lecture de PC et la mise a zéro est activée par la sortie d un transistor Tr piloté les bits du PC et dont l entrée est alimentée par une horloge 6 microns L
Si le bit du PC est a 1 le transistor Tr n est pas passant donc l électron de l horloge L ne met pas a zéro la boucle Linc et ne bloque pas le transistor To donc l électron émis par L atteint la sortie du registre ce qui permet de bien avoir la valeur du PC en sortie de registre malgré la logique d incrémentation.
Si le bit du PC est a 0 le transistor Tr est passant donc l électron de l horloge L met a zéro la boucle Linc et bloque le transistor To donc l électron émis par L n atteint pas la sortie du registre ce qui permet de bien avoir la valeur du PC en sortie de registre malgré la logique d incrémentation. En raison des delais de propagation Linc n est videe qu apres que la valeur qu elle contient ai ete XORee avec le bit du PC donc le bit du PC passe a 1
D un point de vue arithmetique tant que les bits du PC sont a 1 Linc conserve sa valeur a 1 donc le bit du PC est mis a zero par la porte XOR, le premier bit a zero passera lui a 1 tandis que Linc sera videe laissant les bits suivants intacts. Cela correspomd bien a un increment.

Décrit de manière algorithmique on obtient le code suivant pour n=4 avec n le nombre de bits du PC:

// this code start to be executed when reading PC
bool B = true;
// PC value
bool PC_in[8]
// PC value
bool PC_out[8]
 
for(int I = 0 ; i < 8 ; ++i)
{
    PC_out[I] = B ^PC_in[I];
    if(!PC_in[I])
    {
       B = false;
    }
}

Si l on applique ce code a une valeur numérique telle que b00000000 (LSB first) soit 0

Index 0 1 2 3 4 5 6
PC_in[Index] 0 0 0 0 0 0 0
Valeur de B 1 0 0 0 0 0 0
PC_out[Index] 1 0 0 0 0 0 0

Soit une valeur de sortie de b10000000 (LSB first) soit 1 ce qui était attendu

Si l on applique ce code a une valeur numérique telle que b11100000 (LSB first) soit 7

Index 0 1 2 3 4 5 6
PC_in[Index] 1 1 1 0 0 0 0
Valeur de B 1 1 1 1 0 0 0
PC_out[Index] 0 0 0 1 0 0 0

Soit une valeur de sortie de b00010000 (LSB first) soit 8 ce qui était attendu

Registres

Ils sont au nombre de 64 et sont constitues de:

  • Une boucle de période 96 servant a stocker la donnée c est a dire un mot de 16 bits en 6 microns
  • La logique permettant l écriture du registre
  • La logique permettant la lecture du registre

Les registres sont des registres série ce qui signifie qu ils sont lus et écrits bits par bits.

Écriture d un registre

Principe de fonctionnement

Pour écrire dans un registre il faut générer une commande d écriture et présenter une donnée sur l entrée donnée du registre
Dans le WC la donnée arrive par la piste connectée en haut a droite de la banque de registre et va descendre le long de la banque de registres en suivant une cascade de portes logiques
La commande d écriture est générée au niveau d un seul registre, elle doit donc être synchronisée avec la donnée de manière a ce que les deux arrivent sur le registre en même temps et être synchronisées avec la boucle du registre de facon a ce que le 1er bit de la donnée a écrire arrive dans le registre en même temps que le 1er bit de la valeur contenue dans le registre
La commande d écriture va stopper la propagation de la donnée et la "dévier" dans le registre
La commande d écriture est générée par le contrôleur d accès aux registres en écriture par le biais de deux salves d électron

  • Une qui monte par la piste juste a droite de la logique d écriture
  • Une qui redescend entre la piste précédente et la piste data

Les cellules situées en entrée de chaque registre doivent donc assurer les fonctionnalités suivantes:

  • Propagation de la donnée a écrire vers le bas de la banque de registre
  • Propagation des salves montantes et descendantes
  • Envoi de la donnée vers le registre si il y a collision des salves montantes et descendantes

Implémentation

Logique d ecriture dans un registre

Cette logique d écriture est constituée des portes suivantes :

  • 2 portes AND NOT qu on appelle Lsel1 Lsel2
  • Une double porte AND NOT qu on appelle X
  • 2 portes AND NOT qu on appelle 'Lndata1 Lndata2
  • 1 porte OR

L envoi de la donnée vers le registre est effectuée grâce aux deux portes logiques Lsel1 et Lsel2 qui implémentent la fonction suivante:

  • f = (salve montante AND NOT ( salve montante AND NOT salve descendante))

De cette manière f ne vaut 1 que si les deux salves se rencontrent.

La porte X reçoit en entrée la donnée a écrire et f, sa sortie inférieure est reliée a l entrée supérieure de la porte X du registre suivant et les timings d arrivée sont calculés de façon a ce que

  • si f vaut zéro la donnée a écrire est propagée vers le bas de la banque de registre
  • Si f vaut 1 la donnée n est plus propagée vers le bas et la sortie supérieure de X vaut f'=f

La valeur contenue dans le registre traverse la porte OR via l entrée A et f` est branche sur l entrée B ainsi lorsque le registre est sélectionné la valeur contenue précédemment est remplacée par un burst de 16 electrons, si le registre n est pas sélectionné la valeur reste intacte.
Lors de la propagation de la donnée vers le bas de la banque de registre la donnée est présentée en entrée B de la porte logique Lndata1 dont l entrée A reçoit f'.
La sortie de cette porte est elle même envoyée vers l entrée B de la porte logique Lndata2 dont l entrée A reçoit f` et la sortie de cette porte rentre dans le registre.

  • si f' vaut zéro alors Lndata1 recoite la valeur du registre sur l entrée A et 0 sur l entrée B donc la valeur du registre est inchangée
  • si f' vaut 1 alors NOT(NOT(Data)) soit Data arrive sur le registre donc la donnée est chargée dans le registre

Lecture d un registre

Principe de fonctionnement

Pour lire dans un registre il faut générer une commande de lecture et récupérer la donnée en sortie du registre
Dans le WC la donnée repart par la piste située tout de suite a gauche de la banque de registre et va descendre le long de la banque de registres en suivant une cascade de portes logiques
La commande de lecture est générée au niveau d un seul registre. Elle doit être synchronisée avec la boucle du registre de manière a ce que le 1er électron de la salve de commande se présente en sortie du registre en même temps que le 1er bit de la donnée contenue dans le registre
La commande de lecture est générée par le contrôleur d accès aux registres en lecture par le biais de deux salves d électron

  • Une qui monte par la piste juste a gauche de la logique de lecture
  • Une qui redescend a gauche de la piste précédente

Les cellules situées en sortie de chaque registre doivent donc assurer les fonctionnalités suivantes:

  • Propagation de la donnée lue vers le bas de la banque de registre
  • Propagation des salves montantes et descendantes
  • lecture de la donnée du registre si il y a collision des salves montantes et descendantes

Implémentation

Logique de lecture de registre

Cette logique de lecture est constituée des portes suivantes :

  • 2 portes AND NOT qu on appelle Lsel1 Lsel2
  • Une double porte AND NOT qu on appelle X
  • 1 transistor Tr

La lecture de la donnée du registre est effectuée grâce aux deux portes logiques Lsel1 et Lsel2 qui implémentent la fonction suivante:

  • f = (salve montante AND NOT ( salve montante AND NOT salve descendante))

De cette manière f ne vaut 1 que si les deux salves se rencontrent.

La porte X reçoit en entrée gauche f et entrée droite la donnée lue depuis un registre supérieur, sa sortie gauche est reliée a l entrée droite de la porte X du registre inférieur, sa sortie droite est reliée a l entrée du transistor Tr et les timings d arrivée sont calculés de façon a ce que

  • si f vaut zéro la donnée lue est propagée vers le bas de la banque de registre
  • Si f vaut 1 la sortie droite de X vaut f'=f

f' est envoyée sur l entrée du transistor Tr pilote par la valeur contenue dans le registre et sa sortie est reliée a l entrée droite de la porte X du registre inférieur
La sortie du transistor vaut f' si le bit de donnée correspondant du registre vaut 0, en sortie du transistor on obtient donc NOT(donnee du registre)

Registres spéciaux

Ils permettent d effectuer des opérations autres que la simple lecture/écriture.
Les registres SHIFT gauche et droits jouent sur la longueur du chemin parcouru par les électrons jusqu a la sortie du registre pour présenter le bit[1] ou le bit[15] au moment ou le bit[0] devrait se présenter en sortie.
Les registres NOT, AND NOT utilisent respectivement les portes NOT, AND NOT pour implémenter la fonctionnalité

Registre additionneur

Il utilise un additionneur binaire pour calculer la somme des deux registres d entrées
Attention : en cas d overflow la bascule RS qui sert a propager la carrry sera encore settée lorsque les bits[0] des deux registres d entrée vont se représenter a l additionneur, le résultat vaudra donc R60 + R61 + 1
Cette particularité est utilisée dans l algorithme de calcul des nombres premier qui travaille en arithmétique complément a 1. Dans ce cas la -1 se code 0xFFFE et 1 se code 0x0001 ce qui lorsque l on effectue la somme donne 0xFFFF soit le -0 en arithmétique complément a 1
Afin de pouvoir détecter facilement une somme nulle en utilisant le registre conditionnel le programme effectue d’abord une opération en overflow comme par exemple 0xFFF6 + 0xFFFE ( -9 -1 ) ce qui donne 0xFFF5 (-10) avec carry a 1 donc lorsque que l on ajoute 10 on obtient 0xA + 0xFFF5 + 1 = 0x0 qui sera bien détecté a zéro par le registre conditionnel.

Registre conditionnel

Le registre R56 renvoie R55 si R56 non null R57 sinon

Architecture interne

Cette fonctionnalité est implémentée a l aide des composants suivants:

  • 2 bascules RS FF1 et FF2
  • 3 transistors T1,T2,T3
  • Un porte OR
  • Une boucle de reset de même période que la boucle de registre
  • Un générateur d électrons en période 6 microns
Fonctionnement
Registre conditionnel

La boucle de reset met la bascule FF1 a 0 et la bascule FF2 a 1

  • R56 non nul

La valeur stockée dans R56 contient un bit a 1 alors FF1 est settée a 1 ce qui met FF2 a 0
FF2 a 0 T3 est passant donc les electrons émis par le générateur bloquent T2 donc la valeur de R57 n atteint pas la porte OR
FF2 a 1 T1 est passant donc les electrons de R55 atteignent la porte OR

  • R56 nul

La valeur stockée dans R56 ne contient pas de bit a 1 donc FF1 est settée a 0 et FF2 reste a 1
FF2 a 1 T3 est bloquant donc les electrons émis par le générateur n atteigne pas T2 qui est donc passant ce qui laisse la valeur de R57 atteindre la porte OR
FF2 a 1 T1 est bloquant donc les electrons de R55 n atteignent la porte OR

Configuration des registres

Afin de faciliter l exécution d autres programmes que celui fournit par défaut avec le wireworld computer j ai écrit un [Projet_Wireworld#Fichier_de_configuration_g.C3.A9n.C3.A9rique fichier de configuration générique] qui permet de définir le contenu de chacun des registres au démarrage de la simulation via un [Projet_Wireworld#Fichier_de_configuration fichier de configuration].
En faisant générer automatiquement le fichier de configuration correspondant a un programme écrit en assembleur par le modèle fonctionnel du Wireworld Computer il devient facile d écrire des programmes et les exécuter sur le design

Modélisation du WC

Modèle fonctionnel

Le design du Wireworld Computer est complexe et l exécution d une instruction prend plusieurs centaines de générations.
Afin de pouvoir tester et développer facilement des petites programmes pour le wireworld computer j ai développé un modèle fonctionnel en C++ qui reproduit le pipeline d exécution ainsi que le comportement des registres. Il permet aussi de générer le fichier de configuration correspondant qui va charger les registres avec les valeurs nécessaires pour exécuter le programme sur le design du Wireworld Computer.

Entrées/Sorties

Le modèle reçoit en paramètres:

  • le nom d un fichier contenant le programme a exécuter écrit sous une forme d'assembleur
  • un paramètre optionnel --detailled_display pour activer ou non les détails de fonctionnement du convertisseur BCD de l afficheur
  • un paramètre optionnel --instruction_delay pour définir la durée de la tempo ( en ms ) entre chaque instruction
  • un parametre optionnel --output_file pour définir le nom du fichier de configuration et le générer.
Usage is :
        wireworld.exe [OPTIONS] <program_file>
OPTIONS : --<parameter_name>=<parameter_value>
        --detailled_display=...
        --instruction_delay=...
        --output_file=...

Pendant l exécution le modèle fonctionnel affiche les informations suivantes:

  • Numéro du cycle
  • Valeur du PC
  • Valeur de l instruction
  • Mnémonique de l instruction
  • Registre Source => Valeur lue => Registre destination
  • Valeur affichée en cas d écriture dans le registre R0.

Remarque : le modèle fonctionnel ne tient pas compte du délais de latch de la valeur de R0 donc si il y a 2 écritures rapprochées de R0 le modèle fonctionnel indiquera qu elles sont affichées alors que dans le vrai design du Wireworld Computer la 1ere n aurait peut être pas eut le temps d être affichée avant que la seconde ne soit prise en compte.

***** Starting cycle 10*****
PC value :=> PC = 0xb
=> Instruction = 0x3b29
=> MOV R59, R41
R41 => 0x0 => R59
***** Starting cycle 11*****
PC value :=> PC = 0xc
=> Instruction = 0x30
=> MOV R0, R48
R48 => 0x0 => R0
-----------------------------------
** DISPLAY => 0
-----------------------------------
***** Starting cycle 12*****
PC value :=> PC = 0xd
=> Instruction = 0x3d3b
=> MOV R61, R59
R59 => 0x0 => R61

Si l option detailled_display a ete activee la sortie affichera aussi les informations sur le fonctionnement interne du Convertisseur BCD

Format d assembleur

Le format d assembleur utilisé en entrée du simulateur fonctionnel est très simple :

  • Les commentaires commencent par un ; et se terminent par le saut de ligne
  • Il y a une ligne par registre avec la syntaxe suivante
numero_de_registre : [<label_optionnel> :] (Valeur | Instruction ) 

Voici un exemple:

; Register | Action on read                           | Action on write
;----------------------------------------------------------------------------------------
; R0       | Returns zero                             | Writes value to display module
; R1-R52   | Reads value from register                | Writes value to register
; R53      | Returns bitwise AND of R54 with NOT R53  | Writes value to register
; R54      | Returns bitwise AND of R53 with NOT R54  | Writes value to register
; R55      | Returns zero                             | Writes value to register
; R56      | Returns value in R55 if register R56 is  | Writes value to register
;          | non-zero, and the value in R57 otherwise | 
; R57      | Returns zero                             | Writes value to register
; R58      | Returns R58 rotated right one place      | Writes value to register
; R59      | Returns R59 rotated left one place       | Writes value to register
; R60      | Reads value from register                | Writes value to register
; R61      | Returns sum of R60 and R61               | Writes value to register
; R62      | Reads NOT R62                            | Writes value to register
; R63      | Returns program counter value            | Causes branch to given target
;----------------------------------------------------------------------------------------
 0 :       UNUSED
 1 :       MOV R62, R42 ; Compute negative value of upper limit
 2 :       MOV R55, R47 ; Prepare branch if limit non reached
 3 :       MOV R57, R44 ; Prepare branch if limit reached
 4 :       MOV R61, R50 ; Load -1 in adder as second operand
 5 :       MOV R60, R62 ; negative (upper limit - 1) as first operand of adder
 6 :       MOV R60, R61 ; perform addition to set the Carry
 7 :       MOV R61, R41 ; current variable as second operand of adder
 8 :       MOV R56, R61 ; perform addition
 9 :       MOV R63, R56 ; Branch on addition result
10 :       MOV R59, R41 ; Prepare computation of 2 * V
11 :       MOV R0 , R48 ; Display square of variable
12 :       MOV R61, R59 ; Prepare computation of 2 * V + square(V) by setting 2 * V as second operand of adder
13 :       MOV R60, R48 ; Prepare computation of 2 * V + square(V) by setting square(V) as first operand of adder
14 :       MOV R61, R61 ; Compute addition of 2 * V + square(V) and set it as second operand of adder
15 :       MOV R60, R45 ; Preparing addition of increment by setting 1 as first operad of adder
16 :       MOV R48, R61 ; Compute the new square value and store it
17 :       MOV R61, R41 ; Prepare V + 1 by setting V as second operand of adder
18 :       MOV R63, R43 ; Preparing branch at the beginning of the loop
19 :       MOV R41, R61 ; Incrementing current variable
20 :       MOV R63, R44 ; Branching on end of the loop
21 :       MOV R0 , R46 ; End of loop
22 :       0x0000
23 :       0x0000
24 :       0x0000
25 :       0x0000
26 :       0x0000
27 :       0x0000
28 :       0x0000
29 :       0x0000
30 :       0x0000
31 :       0x0000
32 :       0x0000
33 :       0x0000
34 :       0x0000
35 :       0x0000
36 :       0x0000
37 :       0x0000
38 :       0x0000
39 :       0x0000
40 :       0x0000
41 : <V> : 0x0000       ; Initialisation running variable to 0
42 :       0x000a       ; Set upper limit - 1
43 :       0x0004       ; Branch value to restart the loop
44 :       0x0014       ; Branch value to end the loop
45 :       0x0001       ; Increment value
46 :       0xffff       ; Final value
47 :       0x000a       ; Branch value to continue the loop
48 : <SQ>: 0x0000       ; Current square value
49 : <DB>: 0x0000       ; Store double of current value
50 :       0xfffe       ;  -1
51 :       0x0000
52 :       0x0000
53 :       UNUSED
54 :       UNUSED
55 :       UNUSED
56 :       UNUSED
57 :       UNUSED
58 :       UNUSED
59 :       UNUSED
60 :       0x0000
61 :       UNUSED
62 :       UNUSED
63 : <PC>: 0x0001       ; Initial PC

Utilisation

Le modèle fonctionnel m a permis d essayer de développer des programmes pour le wireworld computer.
Par exemple une boucle d affichage des carres des 10 premiers entiers. L intérêt de ce programme est d arriver a calculer des carres successifs malgré l absence d opération de multiplication.
Il se base sur le fait que pow(n+1,2) = pow(n,2) + 2 * n + 1 soit au final 2 additions et un décalage si l on a stocke la valeur du carre précédent.
J ai voulu utiliser cela pour optimiser le programme de calcul de nombre premier utilise dans l exemple de Quinapalus en partant du principe qu il est inutile de chercher les diviseurs superieurs a racine de n.
Comme calculer la racine de n n est pas une opération triviale mon approche a été de partir du carré et de la racine minimum : 1 et 1 puis de calculer le carré et la racine suivante a chaque fois que le nouveau candidat premier a tester devenait supérieur a mon carré courant.
Tout cela afin de limiter le nombre d exécutions de la boucle de soustraction qui simule la division du candidat premier p par le diviseur q
Cela revient a effectuer deux fois moins de divisions par candidat p mais cela complexifie le code:

  • Il faut effectuer un test plus complexe sur p et q : comparaisons par rapport a la racine et au carré ce qui nécessite soustraction et vérification de signe du résultat
  • Cela demande plus d instructions de tests avec la préparation des branchements que cela implique

Étant donnée l architecture URISC du wireworld computer cela s est traduit par une forte inflation du code qui au final a complétement annulé le gain sur le nombre d opérations et fait que l algorithme est légèrement plus lent que l original tout en utilisant la quasi totalité des registres.
Cela m a d ailleurs oblige a bien réfléchir a la manière d écrire le code et a bien utiliser les valeurs d initialisation des registres afin de réussir a faire rentrer le programme dans la banque de registre

Conclusions

Je suis toujours aussi fascine par cet automate cellulaire qui malgré son apparente simplicité permet de simuler des choses aussi complexes qu un "petit" processeur URISC a 64 registres de 16 bits.
Le Wireworld Computer m a permis de découvrir les architectures URISC et TTA que je ne connaissais pas avant et que je compte réutiliser dans mes expérimentations FPGA
Le fait d essayer de développer pour le Wireworld Computer m a permis aussi de prendre conscience des limitations et contraintes induites par cette architecture.
Le reverse engineering du design du Wireworld Computer n a fait que renforcer mon admiration pour ceux qui l ont réalisé et le fait de comprendre maintenant dans le détail comment il fonctionne n’enlève rien a la magie de le voir simuler