Bisher haben wir das Thema diskutiert, wie die Systemgeschwindigkeit mit einigen intensiven Methoden erhöht werden kann. Tatsächlich gibt es jedoch umfangreiche Methoden. Jetzt arbeiten wir mit einer Taktfrequenz von 50 MHz, die mit der Verwendung einer Komponente aus dem Set für ein Universitätsprogramm verbunden ist (und ohne diese ist es unmöglich, SDRAM zu takten, was erfordert, dass die zum Mikrokreislauf führenden Taktimpulse relativ zu den Hauptimpulsen verschoben werden). Als ich diese Komponente in das Spiel einführte, warnte ich, dass diese Lösung nur vorübergehend ist. Dann habe ich so viele neue Informationen auf den Leser geworfen, dass jede zusätzliche Langeweile zu einem Ausruf führen könnte: "Nun, diese FPGAs, hier ist alles so kompliziert!" Jetzt bauen wir bereits einfach und natürlich Prozessorsysteme, all die schrecklichen Dinge liegen hinter uns. Es ist Zeit herauszufinden, wie Sie Ihre eigene Komponente herstellen können, mit der Sie die Taktfrequenz sowohl des Prozessors als auch der daran angeschlossenen Peripheriegeräte erhöhen können.

Frühere Artikel in der Reihe:
- Entwicklung der einfachsten „Firmware“ für in Redd installierte FPGAs und Debugging am Beispiel des Speichertests.
- Entwicklung der einfachsten „Firmware“ für in Redd installierte FPGAs. Teil 2. Programmcode.
- Entwicklung eines eigenen Kerns zur Einbettung in ein FPGA-basiertes Prozessorsystem.
- Entwicklung von Programmen für den Zentralprozessor Redd am Beispiel des Zugriffs auf das FPGA.
- Die ersten Experimente am Streaming-Protokoll am Beispiel der CPU- und Prozessorkommunikation im Redd FPGA.
- Merry Quartusel oder wie der Prozessor zu einem solchen Leben gekommen ist.
- Codeoptimierungsmethoden für Redd. Teil 1: Cache-Effekt.
- Codeoptimierungsmethoden für Redd. Teil 2: Nicht zwischengespeicherter Speicher und paralleler Busbetrieb.
Ein bisschen theoretisches Denken
Lassen Sie uns abschätzen, welche Frequenz wir schmerzlos für die Taktung unseres gesamten Eisens einstellen können. Der im Komplex verwendete SDRAM-Chip ermöglicht eine Grenzfrequenz von 133 MHz. Informationen zu den Taktraten des Prozessors finden Sie in den
Nios II-Leistungsbenchmarks . Dort ist für unseren FPGA Cyclone IV E die Nios II / f-Kernfrequenz von 160 MHz garantiert. Ich bin kein Befürworter des Auspressens aller Säfte aus dem System, daher werden wir über das Arbeiten mit einer Frequenz von 100 MHz sprechen.
Um ehrlich zu sein, habe ich mich immer noch nicht von der in Abschnitt
32.7 angegebenen Methode zur Berechnung der Taktfrequenzverschiebung inspirieren lassen
. Überlegungen zu Uhrzeit, PLL und Timing des IP-Benutzerhandbuchs für eingebettete Peripheriegeräte , aber ich bin anscheinend nicht der einzige. Zumindest führte mich eine lange Suche im Internet nicht zu Artikeln, die Ergebnisse enthielten, die auf die gleiche Weise berechnet wurden, jedoch nicht für die im Hauptdokument angegebene Häufigkeit (dieselben 50 MHz).
Es gibt einen interessanten Artikel, zu dem ich einen direkten Link geben werde:
www.emb4fun.de/fpga/nutos1/index.html . Man könnte sich einfach darauf beziehen und sagen "Lass uns als Autor handeln", wenn nicht für ein "aber": Der Autor dieses Artikels verwendet den PLL-Block (auf Russisch - PLL und auf Haushaltsebene - einen Frequenzumrichter), der einfügt seinen eigenen Code in VHDL. Wie bereits im
Artikel über das lustige Quartusel erwähnt ,
halte ich an der Ideologie fest, dass das Prozessorsystem auf der obersten Ebene der Projekthierarchie stehen sollte. In keiner Sprache, sei es VHDL oder Verilog, sind Einfügungen erforderlich. Vor kurzem hat mein Ansatz eine weitere Bestätigung erhalten: Wir haben einen neuen Mitarbeiter, einen Studenten, der noch kein Verilog spricht, aber großartigen Code für den Redd-Komplex erstellt, da der gewählte Ansatz dies zulässt.
Es stellt sich heraus, dass wir nur davon ausgehen, dass alles für den Autor mit einer Verschiebung von minus 54 Grad funktioniert (welche Art von Grad wird im Artikel beschrieben, dem Link, zu dem ich den obigen Absatz gegeben habe).
Beachten Sie als Nächstes einen weiteren interessanten Artikel
asral.unimap.edu.my/wp-content/uploads/2019/07/2019_IJEECS_Chan_Implementation-Camera-System.pdf . Alles funktioniert für Autoren bei einer Verschiebung von minus 65 Grad.
Versuchen wir, unser System mit einem Wert aus diesem Bereich zu erstellen. Wenn während des täglichen RAM-Tests keine einzige Fehlfunktion auftritt, belassen wir diesen Wert als Kampf. Wir haben das Recht, da die für Redd entwickelte „Firmware“ nicht an die Kunden geht, sondern für interne Bedürfnisse und in Stückzahlen verwendet wird. Wenn überhaupt, ist es immer möglich, alles ohne unnötige Schwierigkeiten zu reparieren (Schwierigkeiten treten auf, wenn die „Firmware“ in Tausenden von verkauften Geräten und einfach von einem Remote-Kunden aktualisiert werden muss).
Neue Hardwarekonfiguration
Aus irgendeinem Grund scheint es mir, dass das Prozessorsystem für diesen Artikel einfacher von Grund auf neu zu erstellen ist als das alte neu zu erstellen. Ich möchte lieber noch einmal von Anfang an alles zeigen, wie man den Prozess „Twist, Twist, wollen verwirren“ demonstriert und dabei ständig auf frühere Artikel verweist. Gleichzeitig reparieren wir das Material. Also, fangen wir an.
Ganz am Anfang wird uns ein völlig leeres System gezeigt, das nur eine Takt- und Rücksetzsignalquelle enthält.

Normalerweise ändere ich dort nichts, aber heute mache ich eine Ausnahme. Ich möchte mich nicht von der Rücksetzschaltung ablenken lassen, da wir weiterhin unter dem Debugger arbeiten werden. Daher werde ich die Rücksetzbedingung vom Pegel auf eine negative Differenz umschalten und das Bein selbst wird anschließend aufgehoben.

Hier hat das Taktsignal jedoch eine Frequenz von 50 MHz (diese Frequenz wird durch die Eigenschaften des auf die Platine gelöteten Generators eingestellt). In dem ersten der oben erwähnten Artikel wurde der dem Hauptprojekt hinzugefügte PLL-Block verwendet. Woher bekommen wir es hier? Und hier ist er!

Dies ist der gleiche Block, aber hier müssen wir keinen Code in Verilog oder VHDL einbetten. Alles ist schon für uns eingefügt! Die Einstellung für verschiedene Arten von FPGAs unterscheidet sich zwar etwas mehr als vollständig. Genauer gesagt sind die einstellbaren Parameter mehr oder weniger gleich, befinden sich jedoch an grundlegend unterschiedlichen Stellen in den Konfigurationsdialogen. Da das Cyclone IV E FPGA im Redd-Komplex verwendet wird, wird die Konfiguration dieser Option in Betracht gezogen.
Ersetzen Sie auf der ersten Registerkarte die Eingangsfrequenz durch 50 MHz (standardmäßig 100) und wechseln Sie zur nächsten Registerkarte (klicken Sie auf Weiter, für Cyclone IV E müssen wir dies viele Male tun).

Deaktivieren Sie die zusätzlichen Ein- und Ausgänge. Wir brauchen sie nicht:

Wir überspringen die nächsten Registerkarten, bis wir den Ausgang C0 einstellen. Dort schalten wir das Optionsfeld um, um die Frequenz einzustellen und den Wert von 100 MHz einzugeben:

Mit C1 sind die Dinge etwas komplizierter. Aktivieren Sie zunächst das Kontrollkästchen, das besagt, dass es auch verwendet werden soll. Zweitens stellen wir auf ähnliche Weise die Frequenz von 100 MHz ein. Nun, und drittens stellen wir die Frequenzverschiebung ein. Welches soll man fragen? Minus 58 oder minus 65? Natürlich habe ich beide Optionen ausprobiert. Beide haben mich verdient. Das Argument zum Thema minus 58 sieht jedoch etwas weniger überzeugend aus. Daher empfehle ich hier, den Wert minus 65 Grad einzugeben (während die Automatisierung mir sagt, dass der tatsächlich erreichte Wert minus 63 Grad beträgt).

Nun, das ist es. Jetzt können Sie die Schaltfläche
Weiter bis zum Ende durchlaufen oder einfach auf
Fertig stellen klicken. Wir verbinden die Eingänge
inclk_interface und
inclk_interface_reset . Der Ausgang
c0 wird als Takt für das gesamte System verwendet. Der Ausgang
c1 wird zum Takten des
SDRAM- Chips exportiert. In Zukunft müssen Sie daran denken, den Datenbus an den Eingang
pll_slave anzuschließen . Für Cyclone V wäre dies nicht erforderlich.

Andere Hardware-Teile, ausschließlich zur Befestigung von Material
Fügen Sie einen Prozessorkern hinzu. Heute wird unser SDRAM getestet. Der Code sollte sich also nicht darin befinden. Dies bedeutet wiederum, dass sich der gesamte Code im internen RAM des FPGA befindet. Das heißt, wir benötigen keinen Anweisungscache. Schalten Sie es aus und sparen Sie FPGA-Speicher. Wir verbinden auch einen stark verbundenen Bus mit Anweisungen und Daten. Es sind keine weiteren interessanten Einstellungen für den Prozessorkern erforderlich.

Fügen Sie mit der üblichen Handbewegung zwei Blöcke internen RAM-FPGA hinzu. Einer ist ein Dual-Port mit einer Kapazität von 16 Kilobyte und einer ist ein einzelner Port mit einer Kapazität von 4 Kilobyte. Ich hoffe, jeder erinnert sich daran, wie man sie benennt und wie man sich verbindet. Das letzte Mal, als ich die Reifen mit Blumen hervorheben wollte, vielleicht um das Lesen zu erleichtern, werde ich dies in diesem Artikel tun.

Vergessen Sie nicht, diesen Speicherblöcken spezielle Adressen im persönlichen Bereich zuzuweisen und zu sperren. Lassen Sie
CodeMemory 0x20000000 und
DataMemory 0x20004000 zuweisen.
Nun, fügen wir dem System einen
SDRAM- Block hinzu, richten ihn ein, sowie
JTAG-UART- Blöcke zum Anzeigen von Nachrichten und einen Einzelbit-
GPIO , auf dem wir die tatsächliche Frequenz messen, um sicherzustellen, dass sie zunimmt. Als Referenz sind hier einige nicht offensichtliche Einstellungen:



Insgesamt erhalten wir ein solches System (ich habe den Datenbus hervorgehoben, da er alle externen Geräte scannt):

Wir weisen dem Prozessor Vektoren zu, weisen dem Erzeugungssystem automatisch Adressen zu, unterbrechen automatisch Interrupt-Nummern.
Wir verbinden das System mit dem Projekt, führen eine grobe Montage durch, weisen die
Beinnummern zu und dieses Mal machen wir nicht nur
CKE virtuell, sondern auch
reset_n (wie dies gemacht wird, habe ich in
einem der vorherigen Artikel gesagt, suchen Sie dort nach Virtual Pin). Wir machen die Endmontage, füllen die Ausrüstung im FPGA. Das ist alles. Wir haben die Ausrüstung fertiggestellt und gehen zum Software-Teil.
Wir haben BSP für unsere Umwelt eingerichtet
Lassen Sie uns zur Abwechslung ein Projekt erstellen, das auf einer Vorlage basiert, die nicht von
Hello World Small , sondern von
Memory Test Small stammt :

Wenn es erstellt wurde, gehen Sie zum BSP-Editor. Wie üblich deaktivieren wir zunächst die SysID-Prüfung und erlauben die Verwendung von C ++ (obwohl ich diesmal den Dateityp nicht ändere, aber es ist bereits eine Gewohnheit für mich):

Aber das Wichtigste, was wir auf der Registerkarte
Linker Script korrigieren müssen. Die Automatisierung hat erkannt, dass der Befehlsbus nur in den
CodeMemory- Speicher geht,
und hat daher einen Codeabschnitt (
Text. ) Im
CodeMemory- Speicher abgelegt. Aber sie kümmerte sich um uns und platzierte alles andere in der größten Datenregion, die sich im
SDRAM befindet . Woher wusste sie, dass wir diese Erinnerung gnadenlos löschen würden?

Wir müssen die Region manuell Zeile für Zeile durch
DataMemory ersetzen (Auswahllisten werden dort
angezeigt , die Auswahl sollte in ihnen neu angeordnet werden). Wir sollten dieses Bild bekommen:

Programmexperimente
Wir verlassen den Editor, generieren den BSP und versuchen, das Programm zum Debuggen auszuführen. Wir erhalten folgenden Text:

Wenn ich die Eingabetaste drücke, war ich nicht erfolgreich. Ich habe etwas eingegeben (ja sogar ein Leerzeichen) und dann die Eingabetaste gedrückt. Dann fragten sie mich:

Stunde für Stunde ist nicht einfacher. Und welche Adresse eingeben? Sie können Platform Designer öffnen und dort den Wert anzeigen. Normalerweise schaue ich in der universellen Referenzdatei system.h nach (der vollständige Pfad für mein Projekt lautet C: \ Work \ CachePlay5 \ software \ MemoryTest_bsp \ system.h). Dort interessieren uns zwei Zeilen:

Gleicher Text#define ALT_MODULE_CLASS_new_sdram_controller_0 altera_avalon_new_sdram_controller #define NEW_SDRAM_CONTROLLER_0_BASE 0x0 #define NEW_SDRAM_CONTROLLER_0_CAS_LATENCY 3 #define NEW_SDRAM_CONTROLLER_0_CONTENTS_INFO #define NEW_SDRAM_CONTROLLER_0_INIT_NOP_DELAY 0.0 #define NEW_SDRAM_CONTROLLER_0_INIT_REFRESH_COMMANDS 2 #define NEW_SDRAM_CONTROLLER_0_IRQ -1 #define NEW_SDRAM_CONTROLLER_0_IRQ_INTERRUPT_CONTROLLER_ID -1 #define NEW_SDRAM_CONTROLLER_0_IS_INITIALIZED 1 #define NEW_SDRAM_CONTROLLER_0_NAME "/dev/new_sdram_controller_0" #define NEW_SDRAM_CONTROLLER_0_POWERUP_DELAY 100.0 #define NEW_SDRAM_CONTROLLER_0_REFRESH_PERIOD 15.625 #define NEW_SDRAM_CONTROLLER_0_REGISTER_DATA_IN 1 #define NEW_SDRAM_CONTROLLER_0_SDRAM_ADDR_WIDTH 0x18 #define NEW_SDRAM_CONTROLLER_0_SDRAM_BANK_WIDTH 2 #define NEW_SDRAM_CONTROLLER_0_SDRAM_COL_WIDTH 9 #define NEW_SDRAM_CONTROLLER_0_SDRAM_DATA_WIDTH 16 #define NEW_SDRAM_CONTROLLER_0_SDRAM_NUM_BANKS 4 #define NEW_SDRAM_CONTROLLER_0_SDRAM_NUM_CHIPSELECTS 1 #define NEW_SDRAM_CONTROLLER_0_SDRAM_ROW_WIDTH 13 #define NEW_SDRAM_CONTROLLER_0_SHARED_DATA 0 #define NEW_SDRAM_CONTROLLER_0_SIM_MODEL_BASE 0 #define NEW_SDRAM_CONTROLLER_0_SPAN 33554432 #define NEW_SDRAM_CONTROLLER_0_STARVATION_INDICATOR 0
Dabei ist die Dezimalzahl 33554432 gleich hex 0x2000000. Daher sollten meine Antworten und das Ergebnis der Arbeit folgendermaßen aussehen:

Großartig, aber das ist nicht gut für einen täglichen Test. Ich habe die
Hauptfunktion folgendermaßen umgeschrieben:
int main(void) { int step = 0; while (1) { if (step++%100 == 0) { alt_printf ("."); } if (MemTestDevice(NEW_SDRAM_CONTROLLER_0_BASE, NEW_SDRAM_CONTROLLER_0_SPAN)!=0) { printf ("*"); } } return (0); }
Die Punkte zeigen an, dass das Programm nicht „eingefroren“ ist. Wenn ein Fehler auftritt, wird ein Sternchen angezeigt. Aus Gründen der Zuverlässigkeit können Sie einen Haltepunkt für die Ausgabe festlegen und ihn dann einfach nicht in den Ruhezustand versetzen.
Zwar kletterten die „linken“ Punkte von irgendwoher. Es stellte sich heraus, dass sie in der Funktion
MemTestDevice () angezeigt werden. Dort habe ich ihre Schlussfolgerung gelöscht. Der Test war erfolgreich. Das resultierende System kann zumindest für interne Bedürfnisse verwendet werden (solche Entwicklungen werden nämlich im Rahmen des Redd-Komplexes durchgeführt).
Überprüfen der Systemleistung
Aber ich bin bereits daran gewöhnt, dass man bei der Arbeit mit Geräten nichts vertrauen kann. Alles sollte sorgfältig geprüft werden. Stellen wir sicher, dass wir mit einer Frequenz arbeiten, die im Vergleich zu früheren Artikeln verdoppelt wurde. Fügen Sie die bekannte Funktion MagicFunction1 () hinzu.
Lass mich dich daran erinnern, wie sie aussieht. void MagicFunction1() { IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); }
Wir werden es von
main () nennen , wir werden die Impulse auf dem Oszilloskop erfassen, aber dieses Mal werden wir nicht nur auf ihre Schönheit, sondern auch auf die Frequenz achten (ich möchte Sie daran erinnern, dass jeder Tropfen, auch nach oben oder unten, ein Befehl ist, damit Sie den Abstand zwischen den Tropfen messen können )

Nur 50 Megahertz. Hat sich die Frequenz wirklich nicht erhöht? Vergleichen Sie mit der Häufigkeit des Codes, der beim Schreiben des letzten Artikels entwickelt wurde, und wir verstehen, dass alles in Ordnung ist. Es ist nur so, dass die normale Pio-Einheit 2 Taktzyklen pro Ausgang zum Port benötigt (bei einem hausgemachten habe ich 1 Takt, aber hier reicht es aus, um sicherzustellen, dass sich die Leistung des Systems verdoppelt).

Fazit
Anstatt einen Oszillator mit fester Frequenz zu verwenden, haben wir gelernt, wie man eine benutzerdefinierte PLL-Einheit verwendet. Die erkannten Konstanten sind zwar für eine Frequenz von 100 MHz vorgesehen, aber jeder kann sie entweder mit bekannten Berechnungen oder durch Ausprobieren auf eine andere Frequenz einstellen. Wir haben auch die Fähigkeiten zur Schaffung eines optimalen Prozessorsystems gestärkt und sichergestellt, dass der Speicher bei einer höheren Frequenz stabil funktioniert und die Frequenz wirklich zunimmt.
Im Allgemeinen können wir bereits alle Computer-Dinge produzieren, wir können sogar mit dem Zentralprozessor austauschen, aber der Zentralprozessor des Komplexes wird triviale Berechnungen effizienter bewältigen. Redd wird FPGA hinzugefügt, um Hochgeschwindigkeitsschnittstellen zu implementieren oder Informationsflüsse zu erfassen (gut oder abspielen). Wir haben die Grundlagen des Entwerfens bereits beherrscht und gelernt, mehr oder weniger hohe Leistung zu erzielen. Es ist Zeit, weiter mit Schnittstellen zu arbeiten, was wir im nächsten Artikel tun werden. Genauer gesagt, eine Reihe von Artikeln, die die Regel "ein Artikel - eine Sache" berücksichtigen.