Gray Code מגדיל

כל רכיב הכלול בספריה מוגדר על ידי יצירת תת מחלקה של InstanceFactory שנמצאת ב- החבילה com.cburch.logisim.instance. תת-מחלקה זו כוללת את כל הקוד המעורב

(כאן אנו מתארים את ה-API עבור הגרסה הנוכחית של Logisim. ייתכן שתמצא מספר ספריות שפותחו עבור גרסאות ישנות יותר של Logisim, שבהן פותחו רכיבים על ידי הגדרת שתי מחלקות, אחת המרחיבה את Component ועוד ComponentFactory מרחיב. גרסה 2.3.0 הציגה את InstanceFactory הפשוט הרבה יותר API; הטכניקה הישנה יותר הוצאה משימוש.)

שלוש חבילות Logisim מגדירות את רוב המחלקות הרלוונטיות להגדרת ספריות רכיבים.

com.cburch.logisim.instance

מכיל מחלקות הקשורות ספציפית להגדרת רכיבים, כולל InstanceFactory, מחלקות InstanceState, InstancePainter ו-Instance.

com.cburch.logisim.data

מכיל מחלקות הקשורות לרכיבי נתונים המשויכים לרכיבים, כגון Bounds מחלקה לייצוג מלבנים תוחמים או המחלקה Value לייצוג ערכים יכול להתקיים על חוט.

com.cburch.logisim.tools

מכיל שיעורים הקשורים להגדרת הספרייה.

לגבי קודים אפורים

לפני שנמשיך, הרשו לי לתאר בקצרה את הקוד האפור שעליו מבוססות דוגמאות אלו. זה לא באמת חשוב להבין כיצד דוגמאות אלו פועלות, כך שתוכל לדלג בבטחה לקוד למטה אם תרצה - במיוחד אם אתה כבר מכיר קודי גריי.

קוד אפור הוא טכניקה (על שם פרנק גריי) לאיטרציה דרך רצפי n-bit עם אחד בלבד קצת השתנה עבור כל שלב. כדוגמה, שקול את קוד האפור 4 סיביות המפורט להלן.

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

לכל ערך יש את הסיביות בקו תחתון שישתנה עבור הערך הבא ברצף. לדוגמה, אחרי 0000 מגיע 0001, שבו ה-bit הסופי הוחלף, כך שה-bit הסופי מסומן בקו תחתון.

הרכיבים המובנים של Logisim לא כוללים שום דבר שעובד עם קודי גריי. אבל מעצבי אלקטרוניקה מוצאים קודים אפורים שימושיים לפעמים. מופע בולט במיוחד של קודי גריי הוא לאורך הצירים במפות קרנו.

GrayIncrementer

זוהי דוגמה מינימלית הממחישה את המרכיבים החיוניים להגדרת רכיב. הרכיב הספציפי הזה הוא מגדיל, שלוקח קלט רב-סיביות ומפיק את הקוד האפור הבא אחריו ברצף.

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;

/** This component takes a multibit input and outputs the value that follows it
 * in Gray Code. For instance, given input 0100 the output is 1100. */
class GrayIncrementer extends InstanceFactory {
  /**
   * Unique identifier of the tool, used as reference in project files. Do NOT change as it will
   * prevent project files from loading.
   *
   * Identifier value must MUST be unique string among all tools.
   */
  public static final String _ID = "Gray Code Incrementer";
  
    /* Note that there are no instance variables. There is only one instance of
     * this class created, which manages all instances of the component. Any
     * information associated with individual instances should be handled
     * through attributes. For GrayIncrementer, each instance has a "bit width"
     * that it works with, and so we'll have an attribute. */

    /** The constructor configures the factory. */ 
    GrayIncrementer() {
        super(_ID);
        
        /* This is how we can set up the attributes for GrayIncrementers. In
         * this case, there is just one attribute - the width - whose default
         * is 4. The StdAttr class defines several commonly occurring
         * attributes, including one for "bit width." It's best to use those
         * StdAttr attributes when appropriate: A user can then select several
         * components (even from differing factories) with the same attribute
         * and modify them all at once. */
        setAttributes(new Attribute[] { StdAttr.WIDTH },
                new Object[] { BitWidth.create(4) });
        
        /* The "offset bounds" is the location of the bounding rectangle
         * relative to the mouse location. Here, we're choosing the component to
         * be 30x30, and we're anchoring it relative to its primary output
         * (as is typical for Logisim), which happens to be in the center of the
         * east edge. Thus, the top left corner of the bounding box is 30 pixels
         * west and 15 pixels north of the mouse location. */
        setOffsetBounds(Bounds.create(-30, -15, 30, 30));
        
        /* The ports are locations where wires can be connected to this
         * component. Each port object says where to find the port relative to
         * the component's anchor location, then whether the port is an
         * input/output/both, and finally the expected bit width for the port.
         * The bit width can be a constant (like 1) or an attribute (as here).
         */
        setPorts(new Port[] {
                new Port(-30, 0, Port.INPUT, StdAttr.WIDTH),
                new Port(0, 0, Port.OUTPUT, StdAttr.WIDTH),
            });
    }

    /** Computes the next gray value in the sequence after prev. This static
     * method just does some bit twiddling; it doesn't have much to do with
     * Logisim except that it manipulates Value and BitWidth objects. */
    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; // compute parity of x
        ct = (ct >> 8) ^ ct;
        ct = (ct >> 4) ^ ct;
        ct = (ct >> 2) ^ ct;
        ct = (ct >> 1) ^ ct;
        if((ct & 1) == 0) { // if parity is even, flip 1's bit
            x = x ^ 1;
        } else { // else flip bit just above last 1
            int y = x ^ (x & (x - 1)); // first compute the last 1
            y = (y << 1) & bits.getMask();
            x = (y == 0 ? 0 : x ^ y);
        }
        return Value.createKnown(bits, x);
    }
        
    /** Says how an individual instance should appear on the canvas. */
    public void paintInstance(InstancePainter painter) {
        // As it happens, InstancePainter contains several convenience methods
        // for drawing, and we'll use those here. Frequently, you'd want to
        // retrieve its Graphics object (painter.getGraphics) so you can draw
        // directly onto the canvas.
        painter.drawRectangle(painter.getBounds(), "G+1");
        painter.drawPorts();
    }
        
    /** Computes the current output for this component. This method is invoked
     * any time any of the inputs change their values; it may also be invoked in
     * other circumstances, even if there is no reason to expect it to change
     * anything. */
    public void propagate(InstanceState state) {

        // First we retrieve the value being fed into the input. Note that in
        // the setPorts invocation above, the component's input was included at
        // index 0 in the parameter array, so we use 0 as the parameter below.
        Value in = state.getPort(0);
        
        // Now compute the output. We've farmed this out to a helper method,
        // since the same logic is needed for the library's other components.
        Value out = nextGray(in);
        
        // Finally we propagate the output into the circuit. The first parameter
        // is 1 because in our list of ports (configured by invocation of
        // setPorts above) the output is at index 1. The second parameter is the
        // value we want to send on that port. And the last parameter is its
        // "delay" - the number of steps it will take for the output to update
        // after its input.
        state.setPort(1, out, out.getWidth() + 1);
    }
}

דוגמה זו כשלעצמה אינה מספיקה ליצירת קובץ JAR עובד; עליך לספק גם שיעור ספרייה, כמו מאויר בעמוד הבא.

הבא: שיעור ספרייה.