Debuggen mit Breakpoints

Grundsätzlich

  • Breakpoints werden bei Anweisungen gesetzt
  • Bei Deklarationen und Definitionen werden sie nicht gesetzt

Steuerung des Ablaufs

  • Stoppt das Programm bei einem BREAKPOINT, so ist diese Anweisung nocht nicht ausgeführt. Man sieht den Zustand VOR der Anweisung.
  • STEP OVER: Die aktuelle Zeile wird ausgeführt. Der Cursor springt zur nächsten Zeile.
  • STEP INTO: Das Programm geht IN die Anweisung. Man sieht den Funktionsauftruf , alle Variablen der Funktion und den Ablauf
  • STEP OUT: Der Aufruf wird verlassen und man kehrt zum Ort zurück, an dem der Aufruf stand.
    (Verlassen des Aufrufes kann sehr hilfreich sein, wenn die Funktion sehr lang ist oder ein for-loop auf 20 zählt und man nicht jede Anweisung durchklicken will.)
  • CONTINUE: Man verlasst die aktuelle Codestelle und springt bis zum nächsten Breakpoint.
    Die aktuelle Debugstelle wird verlassen.

keyword protected

public:
Überall kann auf die Variable zugegriffen werden. In anderen Files, in anderen Funktionen, …

private:
Nur in der Klasse selbst kann auf die Variable zugegriffen werden. Innerhalb der Klassenfunktionen oder beim Instanzieren.

protected:
Von aussen kann auf diese Variable nicht zugegriffen werden. Jedoch alle abgeleiteten Objekte, können auch darauf zugreifen. Die Variable gilt nicht nur in 1 Klasse, sondern in allen abgeleiteten Klassen ebenfalls.

Abstrakte Klasse

Abstrakte Klassen enthalten keinen Code. Sie definieren nur die Schnittstelle. Sie können nicht instaziert werden!
Für die Instanzierung muss zuerst eine abgeleitete Klasse gebildet werden, welche die Schnittstellen definiert.
Abstrakte Funktionen sind ein Spezialfall bzw. der extremste Fall einer virtuellen Funktion. Sie werden pure virutal classes genannt.

// nicht instanzierbar
class B {

   public:                // können nur public sein
   virtual void f() = 0;  // = 0, damit pure ist
   virtual void g() = 0;
}

.

// abgeleitet Funktion ist instanzierbar
class B1 : public B {
  public:
      void f() override;   // kein virtual, dafür override
      void g() override;
}

B1 instanz_b;

Abstrakte Klassen sind für die Einheitlichkeit unter Code-Blöcken wichtig. Die Kompatibilität unter Klassen wird erzwungen. (Gleiche Abhandlungen heissen in allen Varianten gleich. )

Functional Test <-> Unit (System) Test

Funktionstest
– Der Kunde beurteilt: Macht das Gerät a und b und c?
– Reines Abfragen von Anforderungen

Systemtest
– Produzenten beurteilen das Gerät: Wird a , b und c korrekt nach den heutigen Spezifikationen verarbeitet?- Wie interagieren die einzelnen Blöcke untereinander ?

Hier das Hausbeispiel als Illustration zum Unterschied unter den beiden Testes (gelber Post)
Hier ein Artikel

JTAG als generelle Schnittstelle

JTAG wurde für Built in Tests auf Chips entwickelt. Da durch JTAG direkt auf den Chip zugegriffen werden kann, erweiterten Hersteller die Schnittstelle für diverse Chip-Bedürfnisse:

JTAG-Schnittstelle als Programmer
JTAG kann gebraucht werden zum Laden eines Programms oder von Konfigurationen (FPFA) über einen Speicher (Flash).
Code kann heruntergeladen und ausgeführt werden.

Einblick in CPU und Peripherals
Hersteller bieten JTAG-Schnittstellen an über die auf die Speichersteine, den Bus, die CPU und weitere Blocks zum Debuggen zugegriffen werden kann. Werte können gesetzt wie auch nur analysiert werden.

Schnittstelle für Logic-Analyser
Die JTAG-Schnittstelle kann für den Zugang von Logic-Analyser-Programen gebraucht werden. Im FPGA ist dies Singaltap.

 

Boundary Scan Test

Ist die am häufigsten verwendete Testmethode in der Digitaltechnik. Das Grundprinzip ist, dass parallel zu den Ein-und Ausgängen FlipFlops zur Abbildung des aktuellen Zustands der Pins, aber auch zur Imitation von Pin-Zuständen eingebaut werden.

Die Flip-Flop Reihe, bilden ein Schieberegister, das die Zustände der IOS abbildet bzw. selbst bildet. Dieses Schieberegister ist SCAN CHAIN genannt.


Um die Daten der Scan Chain  zu erhalten, braucht es die JTAG-Schnittstelle.

Die Scan Chain kann den aktuellen Zustand der IOs über die JTAG-Schnittstelle TDO nach aussen geben.

Sie kann auch virtuell Input Werte setzen über die Schnittstelle TDI. Dies dient der Simulation, bei der ein Verhalten von Eingängen simuliert wird.

 

 

 

JTAG: Built in Testing

Von der Prüfspitze zu Testpins
Früher wurde ausschliesslich mit der „Nadelmethode“ Testnadeln an ausgewählte Stellen (Knoten) angelötet, um den Signalverlauf zu testen.
Da die Bauteile immer kleiner werden ist dies physisch fast nicht mehr möglich. An den Teststellen (Knoten) werden Leitungen an die Oberfläche fix geführt. Ist dies in einem Chip (Built in) so enstehen Pins am Chip-Ausgang, die Zugang zu den Knoten bieten.

JTAG-Standard
Der IEEE 1149.1 Standard ist von der Gruppe JTAG verfasst und definiert eine Schnittstelle für Boundary Scan Test in Chips. Detailierter Artikel JTAG

Über die JTAG-Schnittstelle, auch TAP = Test Access Port genannt kann auf die SCAN CHAIN (siehe Boundary Scan) auf die Ein-und Ausgänge des Chips zugegriffen werden.

scanChain

TAP Schnisstelle
JTAG definiert 4 Signale:
– TCK: Der Clock
– TMS: Der Testmode
– TDI: Die Datenleitung für Input (Simulation)
– TDO: Die Ausgabe des aktuellen Zustands an den Ein-und Ausgängen.

 

..

Swift: Vererbung

  • Alle Swift Objekte sind Subklassen der Klasse NSObjekt oder einer ihrer Subklassen. Die Superklasse wird mit : in der Definition angegeben
    class A: Superklasse.
  • Das Init muss alle Paramter der Superklasse enthalten und weist dieser mit super.init(variable1 = variable1) einen Wert zu
  • Variablen der Superklasse können (ohne super) im init überschrieben werden.  variable2 = 5;
  • Funktionen werden ausserhalb des init() überschrieben und brauchend das keyword override.Bsp der iOS Developper Site
    class NamedShape {
        var numberOfSides = 0
        var name: String
        
        init(name: String) {
            self.name = name     // self für eigene Klassenvariable
        }
        
        func simpleDescription() -> String {
            return "A shape with \(numberOfSides) sides."
        }
    }


    class Square: NamedShape {
        var sideLength: Double
        
        init(sideLength: Double, name: String) {
            self.sideLength = sideLength   // eigene Klassenvariable
            super.init(name: name)    // Variable der Superklasse
            numberOfSides = 4 // Überschreibt Variable der Superklasse
        }
        
        func area() ->  Double {
            return sideLength * sideLength
        }
        
        override func simpleDescription() -> String {
            return "A square with sides of length \(sideLength)."
        }
    }
    let testSquare = Square(sideLength: 5.2, name: "my test square")
    testSquare.area()
    testSquare.simpleDescription()

..

Swift: Constructor

Implizite Variablendeklaration gilt beim Konstruktor nicht

Swift kennt die implizite Variablendeklatration: let a = 5.  So ist die Variable a implizit ein Integer.

class Shape {
  var numberOfSides = 0

  init(numberOfSides:Int){
     self.numberOfSides = numberOfSides
}

 

Instanzierung bedingt Wertzuweisung
Bei der Instanzierung muss dem Argument ein Wert  zugewiesen werden.

var s1 = Shape(numberOfSides: 4);  // numberOfSides = 4

 

Konstruktor einer vererbten Klasse

Der Konstruktor muss mindestens die Argumente der Superklasse enthalten. Kann auch eigene Klassenvariablen zusätzlich auflisten

init(numberOfSides:Int, name:String){
    super.init(numberOfSides = numberOfSides)  // Zuweisung Superklasse
    self.name = name     // Zuweisung zu eigener Klassenvariable
}

Mehr Details zur Vererbung in anderem Beitrag.

 

..

binary output C++

Problem mit << (Standard filestream Operator)

Mit Bitset und << in einen Outputfilestream wurde jedes 0 und jedes 1 zu einem Ascii gewandelt.

std::bitset<16> left (161);
std::bitset<8> symbol(98);

std::ofstream ofs;
ofs.open("treebinary.bin", std::ofstream::binary) 

ofs << left << symbol ;

ofs.close()

Dateigrösse (16 + 8) * 1 Char = 24 Byte. (Faktor 8 zu gross).

Lösung write( Pointer to Data, size)
In der Funktion write kann ein Pointer übergeben werden. Dadurch findet keine Umwandlung in String (durch <<) statt. Die 0 und 1er werden direkt binär geschrieben.

char data[3]    // 3 Bytes

// inhalt von 1 Byte und 2 Bytes
std::bitset<16> left (361);
std::bitset<8> symbol(98);

// array füllen
data[0] = left;
data[2] = symbol;

//array in 1mal ausgeben
std::ofstream ofs; ofs.write((char *) data , 3) 

ofs.close()
  • Alle Bits werden auf einmal geschrieben
  • kein << left << symbol notwendig
  • Man gibt den Pointer mit und die Länge der Daten
char* data[3]    // initalisiert als Pointer

std::bitset<16> left (361);
std::bitset<8> symbol(98);

data[0] = left;
data[2] = symbol;

std::ofstream ofs; ofs.write(data , sizeof() 

ofs.close()
// array of struct
std::struct Node
{  
    uint16_t left = 0;
    uint16_t right = 0;
    uint8_t symbol = 0;
    bool filled = false;
};

Node tree[100];

// füllen des arrays per tree-funktion


// array per Pointer mitgeben, länge = Node-länge * Anzahl
std::ofstream ofs; ofs.write((char*) tree , sizeof( (struct Node) * 100);

ofs.close()