Incrémenteur de code Gray

Chaque composant inclus dans une bibliothèque est défini en créant une sous-classe de InstanceFactory trouvée dans le paquetage com.cburch.logisim.instance. Cette sous-classe contient tout le code nécessaire

Nous décrivons ici l'API de la version actuelle de Logisim. Vous pouvez trouver des bibliothèques développées pour d'anciennes versions de Logisim, dans lesquelles les composants étaient développés en définissant deux classes, l'une étendant Componentet l'autre ComponentFactory . La version 2.3.0 a introduit l'API InstanceFactory beaucoup plus simple ; l'ancienne technique est obsolète.

Trois packages Logisim définissent la plupart des classes nécessaires à la définition des bibliothèques de composants.

com.cburch.logisim.instance

Contient des classes spécifiquement liées à la définition des composants, y compris l'InstanceFactory, InstanceState, InstancePainter, et Instance classes.

com.cburch.logisim.data

Contient des classes relatives aux éléments de données associés aux composants, tels que les éléments suivants : Bounds class pour représenter les rectangles de délimitation ou la Value class pour représenter les valeurs qui peuvent exister sur un fil.

com.cburch.logisim.tools

Contient les classes liées à la définition de la bibliothèque.

À propos des codes Gray

Avant de poursuivre, permettez-moi de décrire brièvement le code Gray sur lequel ces exemples sont basés. Ce n'est pas vraiment important pour comprendre le fonctionnement de ces exemples, vous pouvez donc passer directement au code ci-dessous si vous le souhaitez - en particulier si vous connaissez déjà les codes Gray.

Le code de Gray est une technique (nommée d'après Frank Gray) qui permet de parcourir des séquences de n bits en ne changeant qu'un seul bit à chaque étape. À titre d'exemple, considérons le code de Gray à 4 bits ci-dessous.

0000
0001
0011
0010
       0110
0111
0101
0100
       1100
1101
1111
1110
       1010
1011
1001
1000

Pour chaque valeur, le bit souligné est celui qui sera modifié pour la valeur suivante dans la séquence. Par exemple, après 0000 vient 0001, dans lequel le dernier bit a été basculé, donc le dernier bit est souligné.

Les composants intégrés de Logisim ne comprennent rien qui fonctionne avec des codes Gray. Mais les concepteurs d'électronique trouvent parfois le code de Gray utile. Un exemple particulièrement remarquable de codes de Gray se trouve le long des axes dans les tables de Karnaugh.

GrayIncrementer

Il s'agit d'un exemple minimal illustrant les éléments essentiels de la définition d'un composant. Ce composant particulier est un incrémenteur, qui prend une entrée multibits et produit le code Gray suivant dans la séquence.

package com.cburch.gray;

import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.InstanceFactory;
import com.cburch.logisim.instance.InstancePainter;
import com.cburch.logisim.instance.InstanceState;
import com.cburch.logisim.instance.Port;
import com.cburch.logisim.instance.StdAttr;

/** Ce composant prend une entrée multibits et émet la valeur qui la suit en code
  * Gray. Par exemple, pour une entrée 0100, la sortie est 1100 */
class GrayIncrementer extends InstanceFactory {
  /**
   * Identifiant unique de l'outil, utilisé comme référence dans les fichiers de projet.
   *  Ne PAS modifier car cela empêcherait le chargement des fichiers de projet.
   *
   *  La valeur de l'identifiant doit OBLIGATOIREMENT être une chaîne unique parmi 
   *  tous les outils.
   */ 
  public static final String _ID = "Gray Code Incrementer";
  
   /* Notez qu'il n'y a pas de variables d'instance. Une seule instance de 
    * cette classe est créée, qui gère toutes les instances du composant. 
    * Toute information associée à des instances individuelles doit être 
    * gérée au moyen d'attributs. Pour GrayIncrementer, chaque instance a une
    * "largeur de bit" avec laquelle elle travaille, et nous aurons donc cet 
    * attribut. */
	
    /** Le constructeur configure le factory. */
    GrayIncrementer() {
        super(_ID);
                
        /* C'est ainsi que nous pouvons définir les attributs des GrayIncrementers.
         * Dans ce cas, il n'y a qu'un seul attribut - la largeur - dont la valeur
         * par défaut est 4. La classe StdAttr définit plusieurs attributs 
         * courants, dont un pour la "largeur de bit". Il est préférable 
         * d'utiliser ces attributs StdAttr lorsque cela est nécessaire : 
         * Un utilisateur peut alors sélectionner plusieurs composants 
         * (même provenant de différents factory) avec le même attribut et les 
         *  modifier tous en même temps. */
        setAttributes(new Attribute[] { StdAttr.WIDTH },
                new Object[] { BitWidth.create(4) });
        
        /* Le "décalage des limites" est l'emplacement du rectangle englobant par 
         * rapport à l'emplacement de la souris. Ici, nous choisissons un 
         * composant de 30x30, et nous l'ancrons par rapport à sa sortie principale
         * (comme c'est typiquement le cas pour Logisim), qui se trouve être au
         * centre du bord est. Ainsi, le coin supérieur gauche de la boîte 
         * englobante se trouve à 30 pixels à l'ouest et à 15 pixels au nord de 
         * l'emplacement de la souris. */                
        setOffsetBounds(Bounds.create(-30, -15, 30, 30));
        
        /* Les ports sont des emplacements où des fils peuvent être connectés 
         * à ce composant. Chaque objet port indique où trouver le port par 
         * rapport à l'emplacement d'ancrage du composant, puis si le port est
         * une entrée/sortie/les deux, et enfin la largeur de bit attendue 
         * pour le port. La largeur de bit peut être une constante (comme 1) 
         * ou un attribut (comme ici). */
                                   
        setPorts(new Port[] {
                new Port(-30, 0, Port.INPUT, StdAttr.WIDTH),  
                new Port(0, 0, Port.OUTPUT, StdAttr.WIDTH),
            });
    }
	
     /** Calcule la valeur du code de Gray suivante dans la séquence après la
      * précédente. Cette méthode statique ne fait que manipuler des bits ;
      * elle n'a pas grand-chose à voir avec Logisim, si ce n'est qu'elle 
      * manipule les objets Value et BitWidth. */
    static Value nextGray(Value prev) {
        BitWidth bits = prev.getBitWidth();
        if(!prev.isFullyDefined()) return Value.createError(bits);
        int x = prev.toIntValue();
        int ct = (x >> 16) ^ x; // calculer la parité de x
        ct = (ct >> 8) ^ ct;
        ct = (ct >> 4) ^ ct;
        ct = (ct >> 2) ^ ct;
        ct = (ct >> 1) ^ ct;
        if((ct & 1) == 0) {  // si la parité est paire, retourner le bit de 1
            x = x ^ 1;
        } else { // else flip bit just above last 1
            int y = x ^ (x & (x - 1));  // calculer d'abord le dernier 1
            y = (y << 1) & bits.getMask();
            x = (y == 0 ? 0 : x ^ y);
        }
        return Value.createKnown(bits, x);
    }
	
     /** Indique comment une instance individuelle doit apparaître sur 
         le canevas. */
    public void paintInstance(InstancePainter painter) {
        
        // Il se trouve que InstancePainter contient plusieurs méthodes de 
        // commodité pour le dessin, et nous les utiliserons ici. Souvent, vous
        // voudrez récupérer l'objet Graphics (painter.getGraphics) afin de 
        // dessiner directement sur le canevas. */
        painter.drawRectangle(painter.getBounds(), "G+1");
        painter.drawPorts();
    }
	
    /** Calcule la sortie actuelle pour ce composant.
      * Cette méthode est invoquée chaque fois que l'une des entrées change de
      * valeur ; elle peut également être invoquée dans d'autres circonstances,
      * même s'il n'y a aucune raison de s'attendre à ce qu'elle change 
      * quoi que ce soit. */
    public void propagate(InstanceState state) {
        
        /* Tout d'abord, nous récupérons la valeur introduite dans l'entrée. 
        // Notez que dans l'invocation setPorts ci-dessus, l'entrée du 
        // composant était incluse à l'index 0 dans le tableau de paramètres, 
        // nous utilisons donc 0 comme paramètre ci-dessous. */
        Value in = state.getPort(0);
        
        // Maintenant, calculez la sortie. Nous avons confié cette tâche à 
        // une méthode helper, car la même logique est nécessaire pour les 
        // autres composants de la bibliothèque. */
        Value out = nextGray(in);
        
        // Enfin, nous propageons la sortie dans le circuit. Le premier 
        // paramètre est 1 car dans notre liste de ports (configurée par 
        // l'invocation de setPorts ci-dessus) la sortie est à l'index 1.
        // Le deuxième paramètre est la valeur que nous voulons envoyer 
        // sur ce port. Et le dernier paramètre est son "délai" - 
        // le nombre d'étapes nécessaires pour que la sortie soit mise à jour 
        // après son entrée. */
        state.setPort(1, out, out.getWidth() + 1);
    }
}

Cet exemple ne suffit pas à créer un fichier JAR fonctionnel ; vous devez également fournir une classe Library, comme illustré à la page suivante.

Suite : Library Class.