I denna laboration får du träna på att använda abstrakta klasser, arv, dynamisk bindning, rekursion och generiska containerklasser.

När man konstruerar digitala kretsar utgår man från enkla digitala komponenter, t.ex. logiska grindar (gates) och vippor (flipflops). Komponenternas in- och utsignaler är elektriska spänningar på "låg nivå" eller "hög nivå". Dessa brukar betecknas som 0 och 1 (false resp. true). De enklaste grindarna avbildar på elektronisk väg de logiska funktionerna, t.ex. "icke", "och", "eller" och "exklusiv eller". Vippor kännetecknas av att de har ett inre tillstånd, d.v.s. utsignalernas värden beror inte enbart på insignalernas aktuella värden.

Er uppgift är att skriva några klasser som modellerar och simulerar digitala kretsar och komponenter enligt beskrivningen som följer. I de klasser som beskrivs nedan får ni lägga till instansvariabler och -metoder om det behövs. Dessa ska i så fall vara vara private. Inga klassvariabler eller -metoder ska läggas till. Binära värden ska representeras av boolean, där false representerar 0 och true representerar 1.

Klassen CircuitComponent

Ni ska skriva en klass CircuitComponent, basklassen för komponenter. Tänk på att klasser med abstrakta metoder måste deklarares abstrakta. En komponent har ett visst antal ingångar och utgångar. In- och utgångar adresseras med index från 0 till antalet in-/utgånger - 1. För varje in- och utgång finns ett aktuellt värde (en boolean). Varje utgång är antingen inte kopplad eller kopplad till en viss ingång på en komponent. En kopplad utgång representeras av en instans av hjälpklassen Wire, se nedan. Varje ingång är antingen kopplad eller inte, vilket representeras av en boolean.

De komponenter med internt tillstånd som kan modelleras är tänkta att vara synkront styrda av en klocksignal. För att undvika konstigheter med timing så finns det två metoder. Den ena, updateState, uppdaterar interna tillståndet baserat på ingångsvärdena men ändrar ej utångsvärdena. Den andra, propagateStateChange, beräknar nya utgångsvärden baserat på nya interna tillståndet och propagerar dessa till kopplade komponenter.

Klassen ska innehålla åtminstone dessa medlemmar:

Klassen LogicGate

Denna klass ska ärva CircuitComponent och modellera en logisk grind, d.v.s. en komponent utan internt tillstånd och med en utgång.

Den ska åtminstone dessa medlemmar:

Klasserna AndGate, OrGate, XorGate och NotGate

Dessa klasser ska vara konkreta, ärva LogicGate, ha en konstruktor utan argument och definiera compute på rätt sätt (och, eller, exklusiv eller, icke). NotGate ska ha en ingång, de andra två ingångar. Använd getInput för att läsa ingångarnas värden i definitionen av compute.

Klassen Adder

Det ska vara en konkret klass som ärver CircuitComponent. Den representerar en full-adder, en komponent med tre ingångar och två utgångar som vi kallar s och c. s ska vara utgången med index 0 och c utgången med index 1. Ingångarna tolkas som nollor och ettor och summeras. Resultatet kan beskrivas med ett binärt tal med två bitar. Utgången s (summa) motsvarar minst signifikanta biten i resultatet och c (carry) den mest signifikanta. Klassen ska ha en konstruktor utan argument. Klassen behöver inte implementeras genom att skapa en krets med logiska grindar, utan man kan helt enkelt utföra additionen med Java-kod genom att implementera computeOutputs på rätt sätt.

Klassen Constant

Den modellerar en komponent med bara en utgång vars värde är konstant. Den ska ha följande definition

class Constant extends CircuitComponent {
      private final boolean value;
      
      public Constant(boolean value) {
        super(0, 1);
        this.value = value;
        if (value == true) {
            propagateChange();
        }
      }
      
      protected void computeOutputs(boolean[] newOutputValues) {
        newOutputValues[0] = value;
      }
}

Klassen Input

Ska vara en konkret klass och representera en ingång till en krets av komponenter. Den ska ha noll ingångar och en utgång. Klassen ska ärva CircuitComponent. Den kan definieras ungefär som Constant, men variabeln ska inte vara final utan kunna ändras efter att komponenten skapats.

Klassen ska ha följande:

Klassen Output

Ska vara en konkret klass. Är motsatsen till Input, en utgång i en krets. Den ska ha en ingång och ingen utgång.

Klassen ska ha:

Klassen Fork

Klassen ska motsvara en förgrening med valbart antal utgångar. Definitionen ska vara

class Fork extends CircuitComponent {
    public Fork(int nOutput) {
        super(1, nOutput);
    }
    
    protected void computeOutputs(boolean[] newOutputValues) {
        boolean val = getInput(0);
        for (int i = 0; i < newOutputValues.length; i++) {
            newOutputValues[i] = val;
        }
    }
}

Klassen StatefulComponent

Klassen ska vara en abstrakt superklass för komponenter med tillstånd. Definitionen ska vara

abstract class StatefulComponent extends CircuitComponent {
  StatefulComponent(int nin, int nout) {
   super(nin, nout);
  }
  final protected void propagateStateChange() {
    propagateChange();
  }  
}

propagateStateChange överskuggas med en definition där propagateChange anropas. Dess sub-klasser ska överskugga updateState.

Klassen DFlipFlop

En konkret klass som ska ärva StatefulComponent och simulera en D-vippa. Den ska ha en ingång, en utgång och en bit som internt tillstånd. Det interna tillståndet ska initialt vara false. När updateState anropas ska det interna tillståndet bli lika med ingångsvärdet. När computeOutputs anropas ska utgångsvärdet sättas till det interna tillståndets värdet. Klassen ska ha en konstruktor utan argument.

Klassen JKFlipFlop

En konkret klass som ska ärva StatefulComponent och simulera en JK-vippa. Den ska ha två ingångar (j på index 0 och k på index 1), två utgångar (q på index 0 och nq på index 1) och en bit som internt tillstånd. Det interna tillståndet ska initialt vara false. När updateState anropas ska interna tillståndet

När computeOutputs anropas ska q sättas till det interna tillståndets värdet och nq till icke q. Klassen ska ha en konstruktor utan argument.

Klassen Circuit

Klassen ska representera en hel krets med komponenter.

Den ska ha

Testning

Ni kan testa era komponentklasser innan ni implementerat Circuit genom att skapa och koppla ihop instanser. Lägg till Inputs och Outputs och påverka och avläs värden via deras metoder setValue resp. getValue. Mellan krets-ingångarna och -utgångarna lägger ni in andra komponenter. Se exemplet ovan.

När ni implementerat Circuit kan ni använda detta program för interaktiv testning. Det låter användaren välja en fil som beskriver en krets enligt formatet ovan. För varje kretsingång skapas en knapp med dess namn och värde. När man trycker på knappen ändras ingångens värde. För varje kretsutgång visas dess namn och värde. När man klickar på ingångsknappar ska detta automatiskt och korrekt återspeglas i utgångarnas värden om allt funkar som det ska (förutom för vipp-komponenterna). Det finns också en knapp "Clock Tick" som anropar tick i Circuit, d.v.s. simulerar ett synkront klock-tick hos de komponenter i kretsen som har ett internt tillstånd. För kretsar utan komponenter med tillstånd (vippor) har "Clock Tick" ingen effekt. När man trycker på input-knapparna ska ändringen direkt propageras och synas i utgångarna (om logiken i kretsen innebär att ändring av utgången ska ske).

Det finns några exempel-kretsar att pröva. Många av dem innehåller bara några få olika komponenter, så man kan börja använda detta testprogram tidigt om man implementerar Circuit innan man implementerar alla komponent-klasser. Börja t.ex. med Input, Output och de logiska grindarna.

Slutligen finns ett program för automatisk testning som prövar er implentering med alla typer av komponenter och rapporterar om något fel hittas. Innan ni lämnar in labben bör ni kunna köra detta testprogram utan att fel upptäcks.

Vad ni ska lämna in

Skicka in enbart era .java-filer. De ska vara lösa, ej packade .zip eller dylikt. Skicka inte in testprogrammens java-filer, bara de med komponentklasserna och klassen Circuit. Placera inte era klasser i något paket. (Era filer ska inte innehålla någon rad package ...;)

Tänk på att följa de allmänna riktlinjerna för programmeringsstil som anges på sidan Laborationer.