Deshalb haben wir im
letzten Artikel das einfachste Prozessorsystem entwickelt, mit dessen Hilfe wir den an das FPGA des Redd-Komplexes angeschlossenen RAM-Chip testen wollen. Heute werden wir ein C ++ - Programm für diese Hardwareumgebung erstellen und herausfinden, wie dieses Programm eingefügt und vor allem debuggt werden kann.

Das Programm wird in der Eclipse-Umgebung entwickelt.
Wählen Sie zum Starten den Menüpunkt
Tools-> Nios II Software Build Tools für Eclipse .

Wir müssen ein Projekt und ein BSP dafür erstellen. Klicken Sie dazu im Projekt-Explorer mit der rechten Maustaste und wählen Sie den Menüpunkt
Neu-> Nios II-Anwendung und BSP aus Vorlage .

Die Grundvorlage, auf deren Grundlage das Projekt erstellt wird, wurde bei der Generierung des Prozessorsystems erstellt. Daher finden wir die Datei, die es enthält.

Wir werden dem Projekt auch einen Namen geben (ich habe es
SDRAMtest ) und den Projekttyp auswählen. Ich habe
Hello World Small gewählt . Ich würde gerne
Memory Test wählen, wir führen einen Memory Test durch, aber jetzt überlegen wir uns eine allgemeine Methode zum Erstellen von Anwendungen. Daher wählen wir die allgemeine Option.

Wir haben zwei Projekte erstellt. Das erste ist unser Projekt, das zweite ist BSP (Board Support Package, grob gesagt, Bibliotheken für die Arbeit mit Geräten).

Das erste, was ich normalerweise mache, ist die BSP-Einstellungen zu bearbeiten. Dazu wähle ich das zweite der erstellten Projekte aus, drücke die rechte Maustaste und wähle den Menüpunkt
Nios II -> BSP Editor .

Im Editor ist alles in Gruppen unterteilt:

Aber um nicht entlang zu laufen, werde ich die Wurzel des Baums, das Element
Einstellungen , auswählen und alles linear betrachten. Ich werde auflisten, worauf Sie achten sollten. Bildschirm mit den Grundeinstellungen:

Die Unterstützung für das Beenden und die Unterstützung für das Entfernen beim Beenden ist deaktiviert, wodurch unnötige Teile von großer Größe aus dem Code entfernt werden. Sie werden jedoch zunächst deaktiviert. Diese Dohlen wurden ausgeschaltet, da ich die Mindestoption
Hallo Welt gewählt habe . Wenn Sie andere Codetypen auswählen, sollten Sie auch diese Daten entfernen. Ich kann nicht widerstehen und die C ++ - Unterstützung aktivieren. Es ist auch notwendig, die
SysID- Prüfung zu entfernen, da sonst nichts funktioniert. Tatsache ist, dass ich bei der Erstellung des Hardwaresystems den entsprechenden Block nicht hinzugefügt habe. Die restlichen Einstellungen sind selbsterklärend und wurden nicht geändert. Eigentlich habe ich auch noch keine anderen Einstellungen geändert. Wenig später werden wir hierher zurückkehren, aber jetzt drücken wir die Schaltfläche Generieren. Wir erstellen eine neue Version von BSP basierend auf den vorgenommenen Einstellungen. Klicken Sie am Ende auf Beenden.
Jetzt beginnt der Spaß. So bauen Sie ein Projekt zusammen. Drücken Sie die übliche
Strg + B - wir erhalten eine Fehlermeldung. Dekodierungsfehler Nr.:

Auch in der Konsole ist nichts Vernünftiges:

Wählen Sie das
Build-Projekt für das
SDRAMtest- Projekt aus. Wir erhalten eine angemessene Erklärung:

In der Tat sind nicht funktionierende Vorlagen die Corporate Identity von Quartus. Dies ist ein weiterer Grund, warum ich mich nicht für
Memory Test entschieden habe . Es läuft immer mehr. Hier ist klar, worum es geht.
Diese Funktion der Datei
alt_putstr.c schlägt fehl :
/* * Uses the ALT_DRIVER_WRITE() macro to call directly to driver if available. * Otherwise, uses newlib provided fputs() routine. */ int alt_putstr(const char* str) { #ifdef ALT_SEMIHOSTING return write(STDOUT_FILENO,str,strlen(str)); #else #ifdef ALT_USE_DIRECT_DRIVERS ALT_DRIVER_WRITE_EXTERNS(ALT_STDOUT_DEV); return ALT_DRIVER_WRITE(ALT_STDOUT_DEV, str, strlen(str), 0); #else return fputs(str, stdout); #endif #endif }
Wir werden es später irgendwie beheben (dafür müssen wir das Hardwaresystem komplizieren). Heute brauchen wir es einfach nicht. Ersetzen Sie ihren Körper durch
Rückgabe 0 . Das Projekt beginnt sich zu versammeln.
Großartig. Wir haben bereits eine fertige
Elf-Datei (auch wenn sie keine nützlichen Funktionen ausführt) und wir haben Geräte, mit denen Sie
die Hex-Datei hochladen können (erinnern Sie sich, wie wir dem Quartus-Compiler mitgeteilt haben, dass RAM-Daten durch Einrichten von Onchip RAM daraus geladen werden sollen?). Wie konvertieren wir
Elfen in
Hex ? Sehr einfach. Wir machen uns an das Arbeitsprojekt, drücken die rechte Maustaste und wählen den Menüpunkt
Make Targets-> Build :

Versuchen Sie im
angezeigten Fenster zunächst, die erste Option (
mem_init_install ) auszuwählen:

Nichts wird passieren. Aus der uns gegebenen Fehlermeldung erfahren wir jedoch, wie das Projekt für Quartus abgeschlossen werden kann:
../SDRAMtest_bsp//mem_init.mk:230: *** Deprecated Makefile Target: 'mem_init_install'. Use target 'mem_init_generate' and then add mem_init/meminit.qip to your Quartus II Project. Stop.
Wir
wählen Build zum Zweck von
mem_init_generate aus.
Danach (kurz danach !!!) fügen wir die angegebene
qip-Datei zum Hardwareprojekt hinzu (wir haben bereits gelernt, wie man Dateien hinzufügt, als wir dem Projekt ein Prozessorsystem hinzugefügt haben).

Nun, die
Hex-Datei selbst kann auch mit Ihren Händen gefühlt werden. Da ist er:

Großartig. Wir haben alles, um das Programm mit echten Funktionen zu füllen. Wir gehen zur Datei
hello_world_small.c . Ehrlich gesagt bin ich ein bisschen beleidigt, wenn ich mit reinem C arbeite. Daher werde ich es cpp umbenennen. Damit nichts schief geht, füge ich dem vorhandenen Text einen Zauber hinzu:

Gleicher Text: extern "C" { #include "sys/alt_stdio.h" } int main() { alt_putstr("Hello from Nios II!\n"); /* Event loop never exits. */ while (1); return 0; }
Es ist wichtig, den Vorgang "
Projekt bereinigen" auszuführen, nachdem der Dateityp ersetzt wurde. Andernfalls zeigt der Compiler Fehlermeldungen an, dass die
* .c- Datei aufgrund einiger zwischengespeicherter Informationen nicht gefunden wurde.
Wir werden den Gedächtnistest einfach machen. Ich habe nicht die Aufgabe, dem Leser beizubringen, den RAM-Chip richtig zu testen. Wir stellen nur oberflächlich sicher, dass die Adress- und Datenbusse nicht klebrig sind und keine Unterbrechungen aufweisen. Das heißt, wir schreiben in jede Zelle alle Nullen und die Adresse der Zelle. Unsere Aufgabe ist es, Arbeitscode zu erstellen, und alles andere (andere Füll- und Verzögerungskonstanten, um zu überprüfen, ob die Daten neu generiert werden) sind Implementierungsdetails, die den Text komplizieren, aber sein Wesen nicht ändern.
Beginnen wir mit einer einfachen und natürlichen Frage: „Wo befindet sich das SDRAM im Adressraum?“ Ich erinnere mich, dass wir die automatische Adresszuweisungsfunktion aufgerufen haben, aber nicht einmal untersucht haben, welche Adressen tatsächlich zugewiesen wurden. Tatsächlich werden wir auch jetzt nicht dorthin schauen. Alle notwendigen Informationen sind in der Datei:
... \ SDRAMtest_bsp \ system.h .
Als Ergebnis erhalten wir den folgenden Code: extern "C" { #include "sys/alt_stdio.h" #include <stdint.h> #include "../SDRAMtest_bsp/system.h" #include <altera_avalon_pio_regs.h> } int main() { bool bRes = true; volatile static uint32_t* const pSDRAM = (uint32_t*)NEW_SDRAM_CONTROLLER_0_BASE; static const int sizeInDwords = NEW_SDRAM_CONTROLLER_0_SPAN / sizeof (uint32_t); // for (int i=0;i<sizeInDwords;i++) { pSDRAM [i] = 0; } // for (int i=0;i<sizeInDwords;i++) { if (pSDRAM [i] != 0) { bRes = false; } } // for (int i=0;i<sizeInDwords;i++) { pSDRAM [i] = i; } // for (int i=0;i<sizeInDwords;i++) { if (pSDRAM [i] != i) { bRes = false; } } if (bRes) { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x01); } else { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x02); } /* Event loop never exits. */ while (1); return 0; }
Wir sammeln die
Hex-Datei (ich erinnere Sie daran durch diesen Dialog):

Dann kompilieren wir die Ausrüstung in Quartus und geben den Programmierer ein, um die resultierende „Firmware“ mit dem resultierenden initialisierten Programmcode in den Chip zu laden.

Gießen Sie die „Firmware“ ein und erhalten Sie eine Meldung, dass das System nur dann funktioniert, wenn es mit JTAG verbunden ist (Funktionen der SDRAM-Kernellizenz in der kostenlosen Entwicklungsumgebung). Für den Fall von Redd ist dies eigentlich nicht kritisch. Dieser JTAG ist die ganze Zeit da.

Die RAM-Testergebnisse sind am Stecker, den Kontakten C21 (erfolgreich) oder B21 (Fehler) zu sehen. Wir verbinden sie mit einem Oszilloskop.

Beide Ausgänge sind auf Null. Hier stimmt etwas nicht. Es bleibt zu verstehen, was genau. Tatsächlich ist ein nicht funktionsfähiges Programm einfach großartig, da wir jetzt anfangen werden, das JTAG-Debugging zu lernen. Wir zielen auf das Projekt ab, wählen
Debug As-> Nios II Hardware .

Beim ersten Mal findet das System die Hardware nicht. Es sieht so aus (beachten Sie das rote Kreuz in der hervorgehobenen Registerkartenüberschrift):

Wechseln Sie zur Registerkarte Zielverbindung und aktivieren
Sie das Kontrollkästchen
Nicht übereinstimmende System-ID ignorieren . Bei nachfolgenden Experimenten musste ich manchmal auch den
Zeitstempel "Nicht übereinstimmendes System ignorieren" setzen . Nur für den Fall, ich werde es im Bild nicht mit einem roten, sondern mit einem gelben Rahmen hervorheben, um zu betonen, dass es nicht notwendig ist, es jetzt zu installieren, aber wenn die
Debug- Schaltfläche nicht aktiviert ist, kann es Zeit sein, es zu installieren.
Übernehmen (
Übernehmen ) und dann auf Verbindungen
aktualisieren (diese Schaltfläche ist ausgeblendet, Sie müssen einen Bildlauf durchführen):

In der Liste wird ein Debugger angezeigt. Klicken Sie auf
Debuggen ...

Wir haben bei der
main () -Funktion angehalten. Falls gewünscht, können Sie am Ende des Algorithmus einen Haltepunkt setzen und prüfen, ob das Programm diesen erreicht:

Führen Sie das Programm aus:

Haltepunkt hat nicht funktioniert. Lassen Sie uns das Programm stoppen und sehen, wo es ausgeführt wird.

Alles ist schlecht. Das Programm stürzte eindeutig ab. Wenn Sie nacheinander Haltepunkte anordnen, dann anhalten (mit der roten Schaltfläche „Stop“) und das Programm neu starten (mit der Bug-Schaltfläche), finden Sie den Problembereich.
Alles stirbt, wenn diese Zeile für das allererste Element ausgeführt wird:

Gleicher Text: for (int i=0;i<sizeInDwords;i++) { if (pSDRAM [i] != 0) { bRes = false; } }
Aus Sicht von C ++ ist hier alles sauber. Wenn Sie jedoch den zerlegten Code öffnen, können Sie feststellen, dass dort zu viele einheitliche Befehle vorhanden sind. Es scheint, dass der Code sich selbst gelöscht hat. Und er könnte dies tun, wenn der Linker es in SDRAM einfügt (dessen Inhalt dieser Code überschreibt).

Wir beenden das Debuggen und schließen die Debugging-Perspektive.

Wir gehen zum BSP-Editor, in dem wir uns am Anfang dieses Artikels befanden, aber zur Registerkarte
Linker Script . Wenn ich diese Registerkarte ganz am Anfang korrigieren würde, könnte ich die Technik der Eingabe des JTAG-Debugging nicht zeigen (und die gesamte Leistungsfähigkeit im Vergleich zur einfachen Debugging-Ausgabe hervorheben, da mir die Tatsache des gestauten Codes während des JTAG-Debugging aufgefallen ist). So ist es. Ein guter Linker speichert alles, dessen Größe größer ist.

Wir leiten alles an
onchip_memory weiter ... Jetzt haben wir SDRAM - es ist nur ein Stück Speicher, dessen Leistung uns noch nicht garantiert wurde. Sie können es keinen automatisierten Compileraktionen geben.

Wir bauen
bsp neu
auf , bauen das Projekt neu auf. Muss ich das Speicherabbild neu erstellen und das FPGA überlasten? Dann wird es für halbautonome Arbeit notwendig sein, aber während das Debuggen läuft - nein. Eine neue Version des Programms wird sofort in den Arbeitsspeicher geladen, wenn eine neue Debugging-Sitzung beginnt. Damit beim nächsten Laden des FPGA der Debugger nicht gestartet werden muss, muss am Ende des Debuggens eine neue
HEX-Datei erstellt werden, und es ist wünschenswert, die FPGA- „Firmware“ damit zusammenzusetzen.
Für den neuen Code wurde ein Haltepunkt erreicht, das Testergebnis ist
wahr :

Das Oszilloskop hat sich amüsiert: Der gelbe Strahl ist in Einheit geflogen.

Test bestanden. Lassen Sie uns etwas knifflig werden und gleichzeitig die Systemleistung überprüfen. Machen wir das Finale so:
// GPIO if (bRes) { while (true) { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x01); IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x00); } } else { while (true) { IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x02); IOWR_ALTERA_AVALON_PIO_DATA (PIO_0_BASE,0x00); } }
Infolgedessen haben wir einen solchen Mäander am Oszilloskop (alles wurde durch eine lange Schleife verbunden, so dass eine kapazitive Spitze in der zweiten Zeile erschien, achten Sie nicht darauf):

Das Ergebnis ist eine Frequenz von ungefähr 8,3 MHz bei einer Taktfrequenz von 50 MHz, ohne den Prozessorkern zu optimieren und den Programmcode zu optimieren. Das heißt, der Zugriff auf die Ports erfolgt mit einer Frequenz, die etwas höher als 16 MHz ist, was einem Drittel der Systemfrequenz entspricht. Nicht, dass es völlig anders gewesen wäre, aber besser als 4 MHz bei einer Taktfrequenz von 925 MHz für Cyclone V SoC ... Nein, der NIOS II-Prozessor selbst ist um ein Vielfaches langsamer als der Cyclone ARM mit fünftem Kern, aber der Prozessor, wie gesagt , wir haben x64 im System, hier brauchen wir den Kernel mehr, der die Logik des Eisens liefert. Und diese Logik wird genau durch die Arbeit mit Ports bereitgestellt. Wenn die Arbeit mit Ports langsam ist, ist alles andere regelmäßig inaktiv und wartet darauf, dass der Bus seine Arbeit beendet. Die aufgedeckten Merkmale sind die Zugriffsbeschränkung des Prozessors auf die Ports, jedoch nicht auf die gesamte Hardware. Wie die Arbeit als Ganzes umgesetzt werden soll, werden wir im nächsten Artikel betrachten.
Fazit
Der Artikel zeigt, wie Sie ein Projekt in C ++ für die einfachste Prozessorumgebung erstellen und konfigurieren, die für den Redd-Komplex entwickelt wurde. Die Methoden für den Zugriff auf das Gerät sowie die JTAG-Debugging-Technik werden gezeigt. Laden Sie hier das Hardware- / Software-Kit herunter, das Sie beim Schreiben des Artikels erhalten
haben .