Bauen Sie Ihren eigenen Mikrocontroller

Dieser Artikel konzentriert sich auf das Erlernen der Konstruktion eines Mikrocontrollerkerns und ist nur für Bildungszwecke gedacht. Bitte besuchen Sie www.zilog.com überprüfen Sie die Produktlinie des Herstellers, um einen Mikrocontroller auszuwählen, der Ihren Projektanforderungen entspricht (von Acht-Bit-Z8-Zugaben! und eZ80 Beifall zu die 32-bit ARM Cortex-M3 basierend ZNEO32! b. erweiterte Motorsteuerungsfunktionen).

Meine Liebe zu Mikrocontrollern und Mikroprozessoren begann 1988, als ich an der CEFET-PR (einer vierjährigen brasilianischen Sekundar- / Fachschule und Universität in Curitiba) auf ein technisches Studium hinarbeitete. Ich begann mit dem Erlernen der Grundlagen, während ich den klassischen Zilog Z-80 erforschte (Abbildung 1a).

ABBILDUNG 1A. Der Zilog Z-80A (mit freundlicher Genehmigung von Wikimedia Commons).

Schneller Vorlauf durch eine Karriere der Programmierung, die das Verfassen einiger Bücher über Mikrocontroller-Programmierung (siehe Ressourcen), das Starten eines kleinen Designhauses (ScTec) und das Beenden eines Postgraduiertenprogramms an der CEFET-SC (einer anderen brasilianischen Universität in Florianopolis) beinhaltete. Dies war im Jahr 2008, als ich mehr Kontakt mit programmierbarer Logik und VHDL hatte und meine Neugierde ihren Höhepunkt erreichte. Jahre später, im Jahr 2016, fand ich ein sehr erschwingliches FPGA-Kit (Field-Programmable Gate Array) und beschloss, ihm eine Chance zu geben.

Was wäre besser, als einen Softcore zu entwerfen, um mehr über VHDL (VHSIC Hardware Description Language), FPGAs und Mikroprozessorkerne selbst zu erfahren? Am Ende habe ich mich für einen modernen Z-80-Verwandten entschieden: den Zilog Z8 Encore! (alias, eZ8; Abbildung 1b).

ABBILDUNG 1B. Zilog eZ8.

Es ist ein Acht-Bit-Mikrocontroller-Kern mit einem einfachen, aber leistungsstarken Befehlssatz und einem sehr schönen On—Chip—Debugger. Mit seiner leichtgewichtigen IDE (Integrated Development Environment) und dem kostenlosen ANSI C-Compiler ist es ein hervorragendes Projekt, um eingebettete Systeme zu lernen (und auch zu lehren).

Bevor wir in die Tiefen des Kernbetriebs, der VHDL und der FPGAs eintauchen, werfen wir einen Blick auf den Zilog Z8 Encore! Ausstattung.

ABBILDUNG 1C. FPz8 auf einem FPGA.

Zilog Z8 Zugabe!

Der eZ8 ist eine Acht-Bit-Mikrocontroller-Familie, die auf der erfolgreichen Z8-Familie von Zilog und dem großartigen Z-80-Erbe basiert. Es verfügt über eine Harvard CISC-Maschine mit bis zu 4.096 Byte RAM (Dateiregister- und Sonderfunktionsregisterbereich), bis zu 64 KB Programmspeicher (normalerweise Flash-Speicher) und bis zu 64 KB Datenspeicher (RAM). Der eZ8-Kern enthält außerdem einen vektorisierten Interrupt-Controller mit programmierbarer Priorität und einen On-Chip-Debugger, der über asynchrone serielle Kommunikation mit dem Host-Computer kommuniziert. Diese Mikrocontroller sind mit einem sehr schönen Peripherie-Set ausgestattet, das von vielseitigen 16-Bit-Timern bis hin zu Motorsteuerungs-Timern, von mehreren UARTs (IrDA-fähig) bis hin zu USB-Geräten und vielem mehr reicht (Besuch www.zilog.com um die vollständige Produktlinie zu überprüfen).

Ein Hauptmerkmal des eZ8-Programmiermodells ist das Fehlen eines festen Akkumulators. Stattdessen kann jede der 4.096 möglichen RAM-Adressen als Akkumulatoren arbeiten. Die CPU behandelt ihren Haupt-RAM (die Datei und SFRs — special function registers — area) als eine große Menge von CPU-Registern. Um dies zu erreichen, wird der RAM in Registergruppen aufgeteilt (es gibt 256 Gruppen mit jeweils 16 Arbeitsregistern). Eine Anweisung arbeitet normalerweise innerhalb einer einzigen Arbeitsregistergruppe, die von einem SFR mit dem Namen RP (Register Pointer) ausgewählt wird. Beachten Sie, dass sich alle SFRs auf der letzten Seite des RAM befinden (Adressen ab 0xF00 bis 0xFFF).

In Bezug auf den Befehlssatz gibt es 83 verschiedene Anweisungen, die in zwei Opcodeseiten unterteilt sind. Es umfasst übliche Anweisungen für grundlegende Operationen wie Addition, Subtraktion, logische Operationen, Datenmanipulationsanweisungen, Verschiebungsanweisungen, Flussänderungsanweisungen, einige 16-Bit-Anweisungen, Bitprüfung und -manipulation, 8×8-Multiplikation usw.

Der Programmspeicherbereich ist so organisiert, dass die ersten Adressen speziellen Zwecken gewidmet sind. Die Adressen 0x0000 und 0x0001 sind den Konfigurationsoptionen gewidmet; Die Adressen 0x0002 und 0x0003 speichern den Reset-Vektor; und so weiter. Tabelle 1 zeigt die Programmspeicherorganisation.

0x0000 Option bytes
0x0002 Reset vector
0x0004 WDT vector
0x0006 Illegal instruction vector
0x0008 to 0x0037 Interrupt vectors
0x0038 to 0xFFFF User program memory area

TABLE 1. Simplified program memory organization.

Einige Geräte enthalten auch einen zweiten Datenraum (bis zu 65.536 Adressen), auf den nur mithilfe von LDE / LDEI-Anweisungen zugegriffen werden kann. Dieser Bereich kann verwendet werden, um weniger verwendete Daten zu speichern (da das Lesen / Schreiben langsamer ist als der RAM / SFR-Bereich).

FPz8

Die erste Implementierung von FPz8 verwendet einen sehr konservativen und fest verdrahteten Designansatz mit zwei Hauptbussen: einen für den Programmspeicher und einen für den Registerspeicher. Da ich mich entschieden habe, keinen Datenspeicherbereich einzuschließen, sind die LDE / LDEI-Anweisungen nicht implementiert.

Die Programmspeicherbusse umfassen einen 16-Bit-Befehlsadressbus (IAB), einen Acht-Bit-Befehls-Datenbus (IDB zum Lesen von Daten aus dem Programmspeicher), einen Acht-Bit-Befehls-Schreib-Datenbus (IWDB zum Schreiben von Daten in den Programmspeicher) und ein PGM_WR-Signal, das das Schreiben in den Programmspeicher steuert. FPz8 enthält 16.384 Byte Programmspeicher, der mit synchronem Block-RAM implementiert ist (was bedeutet, dass der Programmspeicherinhalt verloren geht, wenn das Gerät ausgeschaltet wird).

Die fünf Registerbereichsbusse umfassen drei für den Dateiregisterbereich (Benutzer-RAM) und zwei weitere für spezielle Funktionsregister. Es gibt einen 12-Bit-Dateiregisteradressbus (FRAB), einen Acht-Bit-Dateiregistereingangsdatenbus (FRIDB), einen Acht-Bit-Dateiregisterausgangsdatenbus (FRODB), einen Acht-Bit-Registereingangsdatenbus (RIDB) und schließlich einen Acht-Bit-Registerausgangsdatenbus (RODB) zum Schreiben in SFRs. Der FPz8 enthält 2.048 Byte Benutzer-RAM-Speicher, der unter Verwendung von synchronem Block-RAM implementiert ist.

Abbildung 2 zeigt ein Blockschaltbild des FPz8; sie können die CPU, zwei Speichereinheiten (eine für die Programmspeicherung und die andere für die Datenspeicherung) sowie ein externes Timer-Modul sehen.

ABBILDUNG 2. FPz8 block diagramm.

Beachten Sie, dass ich in diesem Projekt keine bidirektionalen Busse für Verbindungen verwende. Unidirektionale Busse sind einfacher zu verwenden, obwohl sie weniger platzsparend sind.

Die VHDL-Beschreibung des FPz8 ist groß und etwas komplex, daher werde ich seine Funktionsweise in einige Module aufteilen, um das Verständnis zu erleichtern:

  1. Instruction Queueing Engine
  2. Instruction decoding
  3. Interrupt processing
  4. Debugger

Instruction Queueing Engine

Das Abrufen von Anweisungen ist eine primäre Aufgabe für jede CPU. Die Harvard-Architektur des FPz8 ermöglicht gleichzeitiges Abrufen und Datenzugriff (aufgrund separater Busse für Anweisungen und Daten). Das bedeutet, dass die CPU einen neuen Befehl abrufen kann, während ein anderer in den Datenspeicher liest oder schreibt.

Die eZ8 hat eine variable länge anweisung wort (anweisung länge variiert von einem byte bis zu fünf bytes); einige Anweisungen sind langwierig, laufen aber schneller als andere. Auf diese Weise hat ein BRK-Befehl eine Länge von einem Byte und läuft in zwei Zyklen, während ein LDX IM,ER1 vier Byte lang ist und in zwei Taktzyklen läuft.

Wie können wir also alle diese Anweisungen erfolgreich dekodieren? Mit einer Befehlswarteschlange; das heißt, ein Mechanismus, der Bytes aus dem Programmspeicher abruft und sie in einem Acht-Byte-Array speichert:

if (CAN_FETCH=’1′) then
if (IQUEUE.FETCH_STATE=F_ADDR) dann
FETCH_ADDR := PC;
IAB <= PC;
IQUEUE.WRPOS := 0;
IQUEUE.RDPOS := 0;
IQUEUE.CNT := 0;
IQUEUE.FETCH_STATE := F_READ;
sonst
wenn (IQUEUE.FULL=’0′) dann
IQUEUE.WARTESCHLANGE(IQUEUE.WRPOS) := IDB;
FETCH_ADDR := FETCH_ADDR + 1;
IAB <= FETCH_ADDR;
IQUEUE.WRPOS := IQUEUE.WRPOS + 1;
IQUEUE.CNT := IQUEUE.CNT + 1;
ende wenn;
ende wenn;
ende wenn;
wenn (IQUEUE.CNT=7) dann IQUEUE.VOLL:=’1′; sonst IQUEUE.VOLL: =’0′;
Ende wenn;

AUFLISTUNG 1. Anweisung Warteschlange Motor.

Das Abrufen wird durch ein Hauptfreigabesignal (CAN_FETCH)gesteuert, das in einigen speziellen Fällen deaktiviert werden kann (Interrupt-Verarbeitung, durch LDC / LDCI-Anweisungen oder Debugger-Zugriff). Es gibt auch eine Struktur (IQUEUE), die mehrere interne Parameter speichert (Abrufstatus, Lese- und Schreibzeiger, Warteschlangenarray selbst, Zähler und vollständiger Indikator).

Der Warteschlangenzähler (CNT) wird verwendet, um die Anzahl der Bytes zu ermitteln, die in der Warteschlange zur Verwendung (Lesen) zur Verfügung stehen. Die Decodierstufe verwendet diese Nummer, um zu überprüfen, ob die gewünschte Anzahl von Bytes für den Befehl bereits in der Warteschlange verfügbar ist.

Befehlsdecodierung

Hier passiert die eigentliche Magie. Der Befehlsdecoder liest Opcodes aus der Befehlswarteschlange und übersetzt sie in entsprechende Operationen.

Das Design des Befehlsdecoders begann mit der Ermittlung der Beziehung zwischen allen Befehlen und den Adressierungsmodi. Auf den ersten Blick ist leicht zu erkennen, dass einige Anweisungen (Abbildung 3) nach Spalten gruppiert sind (DJNZ, JR cc, X, LD r1, IM, JP cc, DA und INC r1). Die Dekodierung eines INC r1-Befehls ist einfach: Bei diesen Einzelbyte-Befehlen gibt das hohe Nibble das Quell- / Zielregister und das untere Nibble den Befehl selbst an (0xE).

ABBILDUNG 3. Opcodes nach Gruppen.

Die meisten Anweisungen können nach einigen Grundregeln klassifiziert werden:

  1. Spalten (das untere Nibble eines Opcodes) geben normalerweise einen Adressierungsmodus an: Anweisungen der Spalte 0x9 verwenden beispielsweise meistens den Adressierungsmodus IM, ER1 und sind vier Byte lang (das zweite Byte ist der unmittelbare Operand und die beiden letzten Bytes sind die erweiterte Zieladresse).
  2. Zeilen (das höhere Nibble eines Opcodes) geben normalerweise eine Operation an: Zeile 0x0 Anweisungen sind meistens Additionsoperationen; Zeile 0x2 Anweisungen sind meistens Subtraktionsoperationen und so weiter.

Wenn wir uns Zeile 0x1 ansehen, können wir sehen, dass die Spalten 0x0 und 0x1 RLC-Anweisungen und die Spalten 0x2 bis 0x9 ADC-Anweisungen sind. Wir können also eine ALU entwerfen, die ein Nibble als Eingabe verwendet (das höhere Nibble aus dem Opcode) und es entsprechend decodiert. Während dies für die Spalten 0x2 bis 0x9 funktionieren würde, würden wir für die ersten beiden Spalten einen anderen Ansatz benötigen.

Deshalb habe ich zwei Einheiten geschrieben: eine ALU, die sich auf die meisten arithmetischen und logischen Anweisungen konzentriert; und eine zweite Einheit (Logische Einheit 2 oder LU2), die andere Operationen ausführt, die in den Spalten 0x0 und 0x1 angezeigt werden (nicht alle Operationen, die in diesen Spalten angezeigt werden, werden von LU2 ausgeführt). Die Betriebscodes für ALU und LU2 wurden so gewählt, dass sie mit den in Abbildung 3 gezeigten Opcode-Zeilen übereinstimmen.

Ein weiteres wichtiges Detail ist, dass alle Anweisungen innerhalb derselben Spalte und Gruppe die gleiche Größe in Bytes haben und daher im selben Decoderabschnitt dekodiert werden können.

Das Decoderdesign verwendet eine große Finite-State-Maschine (FSM), die bei jedem Takttick voranschreitet. Jede Anweisung beginnt in der CPU_DECODE-Statistik. Hier decodiert der Decoder tatsächlich die Opcodes, bereitet Busse und interne Unterstützungssignale vor und führt Schritte zu anderen Ausführungszuständen durch. Unter all diesen Zuständen werden zwei von vielen Anweisungen häufig verwendet: CPU_OMA und CPU_OMA2. Können Sie erraten, warum? Wenn Sie gesagt haben, weil sie mit ALU und LU2 verwandt sind, haben Sie absolut Recht!

OMA steht für One Memory Access und ist der letzte Zustand für alle ALU-bezogenen Anweisungen (ADD, ADC, ADDX, ADCX, SUB, SBC, SUBX, SBCX, OR, ORX, AND, ANDX, XOR, XORX, CP, CPC, TCM, TCMX, TM, TMX und einige Varianten von LD und LDX). Andererseits ist CPU_OMA2 der letzte Status für alle LU2-bezogenen Anweisungen (RLC, INC, DEC, DA, COM, RL, CLR, RRC, SRA, SRL, RR und SWAP).

Werfen wir nun einen Blick in den Status CPU_DECODE. Siehe Abbildung 4.

ABBILDUNG 4. CPU_DECODE Zustand.

Im Status CPU_DECODE können wir sehen, dass viele Aktionen stattfinden. Zu Beginn werden einige temporäre Variablen auf eine Standardbedingung initialisiert. Beachten Sie, dass NUM_BYTES sehr wichtig ist, da es steuert, wie viele Bytes vom Befehlsdecoder verbraucht wurden. Sein Wert wird im letzten Teil dieser Stufe verwendet, um den PC (Programmzähler) zu inkrementieren, den Warteschlangenlesezeiger voranzutreiben und die Anzahl der verfügbaren Bytes in der Warteschlange zu dekrementieren.

Nach dem Abschnitt Initialisierung sehen wir den Abschnitt Interrupt-Verarbeitung. Es ist dafür verantwortlich, anstehende Interrupts zu erkennen und die CPU entsprechend vorzubereiten. Ich werde dies im nächsten Abschnitt behandeln.

Der eigentliche Befehlsdecodierungsblock prüft, ob ein Low-Power-Modus nicht aktiv ist und ob der Debugger-Modus ausgeschaltet ist (OCDCR.DBGMODE=0). Oder im Debug-Modus wurde ein Single Step Debug-Befehl ausgegeben (OCDCR.DBGMODE=1 und OCD.SINGLE_STEP=1). Anschließend werden die verfügbaren Bytes in der Warteschlange überprüft und die Dekodierung fortgesetzt.

Einige Anweisungen (meistens die Singlebyte-Anweisungen) werden im Status CPU_DECODE abgeschlossen, während andere mehrere Zustände benötigen, bis sie vollständig abgeschlossen sind.

Beachten Sie, dass einige Befehlsdecodierungen mehrere Funktionen und Prozeduren verwenden können, die speziell für das FPz8 geschrieben wurden:

  • DATAWRITE – Diese Prozedur bereitet Busse auf einen Schreibvorgang vor. Es wählt aus, ob das Ziel ein interner SFR, ein externer SFR oder ein Benutzer-RAM-Speicherort ist.
  • DATAREAD – Dies ist eine reziproke Funktion für DATAWRITE. Es wird zum Lesen einer Quelladresse verwendet und wählt automatisch aus, ob es sich um einen internen SFR, einen externen SFR oder einen Benutzer-RAM-Speicherort handelt.
  • CONDITIONCODE – Wird für bedingte Anweisungen (wie JR und JP) verwendet. Es benötigt einen Vier-Bit-Bedingungscode, testet ihn und gibt das Ergebnis zurück.
  • ADDRESSER4, ADDRESSER8 und ADDRESSER12 – Diese Funktionen geben eine 12-Bit-Adresse von einer Vier-, Acht- oder 12-Bit-Quelle zurück. Sie verwenden den Inhalt des RP-Registers, um die endgültige 12-Bit-Adresse zu generieren. ADDRESSER8 und ADDRESSER12 prüfen auch, ob ein Escaped-Adressierungsmodus vorliegt.
  • ADDER16 – Dies ist ein 16-Bit-Addierer für die Adressenversatzberechnung. Es nimmt einen Acht-Bit-Operanden mit Vorzeichen, erweitert ihn, fügt ihn der 16-Bit-Adresse hinzu und gibt das Ergebnis zurück.
  • ALU und LU2 – Diese wurden zuvor diskutiert und führen die meisten arithmetischen und logischen Operationen aus.

Interrupt-Verarbeitung

Wie bereits erwähnt, verfügt eZ8 über einen vektorisierten Interrupt-Controller mit programmierbarer Priorität. Zuerst dachte ich, dieser Abschnitt wäre nicht so schwierig, weil Interrupts keine große Sache sind, oder? Nun, als ich anfing herauszufinden, wie man alle benötigten Aufgaben erledigt (Kontext speichern, Vektorisieren, Prioritäten verwalten usw.), wurde mir klar, dass es schwieriger sein würde, als ich zuerst dachte. Nach ein paar Stunden kam ich auf das aktuelle Design.

Das Interrupt-System von FPz8 war einfach. Es hat acht Eingänge (INT0 bis INT7); eine globale Interrupt-Aktivierung (IRQE-Bit im IRQCTL-Register); zwei Register für die Prioritätseinstellung (IRQ0ENH und IRQ0ENL); und ein Register für Interrupt-Flags (IRQ0). Das Design verwendet eine verschachtelte IF-Kette, die bei Erkennung eines Interrupt-Ereignisses bezüglich eines aktivierten Interrupts eine Vektoradresse generiert.

Abbildung 5 zeigt eine komprimierte Ansicht des Interrupt-Systems. Hinweis Es gibt eine erste IF-Anweisung mit dem Symbol ATM_COUNTER . Dies ist ein einfacher Zähler, der vom ATM-Befehl verwendet wird (er deaktiviert Interrupts für drei Befehlszyklen und ermöglicht atomare Operationen).

ABBILDUNG 5. FPz8 unterbrechen system.

Ein letzter Kommentar zu Interrupts: Das Interrupt-Flag-Register (IRQ0) tastet Interrupt-Eingänge bei jeder ansteigenden Flanke des Systemtakts ab. Es gibt auch zwei Puffervariablen (IRQ0_LATCH und OLD_IRQ0), die den aktuellen und letzten Status der Flags speichern. Dies ermöglicht die Erkennung von Interrupt-Flanken und synchronisiert auch die externen Eingänge mit der internen Uhr (FPGAs funktionieren nicht gut mit asynchronen internen Signalen).

On-Chip Debugger

Dies ist wahrscheinlich das coolste Feature dieses Softcore, da es eine kommerzielle integrierte Entwicklungsumgebung (IDE) ermöglicht; b. Zilogs ZDS-II), um auf dem FPz8 laufende Software zu kommunizieren, zu programmieren und zu debuggen. Der On-Chip-Debugger (OCD) besteht aus einem UART mit Autobaud-Fähigkeit und einem daran angeschlossenen Befehlsprozessor. Der UART führt eine serielle Kommunikation mit einem Host-PC durch und liefert Befehle und Daten an die Debugger-Zustandsmaschine, die Debug-Befehle verarbeitet (die Debugger-Befehlsverarbeitung FSM befindet sich im CPU_DECOD-Status).

ABBILDUNG 6. On-Chip Debugger UART (beachten Sie den DBG_RX Synchronizer).

Mein OCD-Design implementiert fast alle Befehle, die auf der realen Hardware verfügbar sind, mit Ausnahme derjenigen, die sich auf den Datenspeicher beziehen (Debug-Befehle 0x0C und 0x0D); der Read Runtime Counter (0x3); und der Read Program Memory CRC (0x0E).

Eine Sache, die ich hervorheben möchte, ist, dass beim Umgang mit asynchronen Signalen in FPGAs Vorsicht geboten ist. Mein erstes Design hat das bei der Verarbeitung des DBG_RX-Eingangssignals nicht berücksichtigt. Das Ergebnis war absolut seltsam. Mein Design hatte in der Simulation einwandfrei funktioniert. Ich habe es auf ein FPGA heruntergeladen und angefangen, mit der seriellen Debug-Schnittstelle mithilfe eines seriellen Terminals herumzuspielen (mein FPGA-Board verfügt über einen integrierten Seriell-USB-Konverter).

Zu meiner Überraschung konnte ich zwar die meiste Zeit erfolgreich Befehle senden und die erwarteten Ergebnisse empfangen, aber manchmal frierte das Design einfach ein und reagierte nicht mehr. Ein Soft-Reset würde die Dinge wieder in ihren ordnungsgemäßen Betrieb bringen, aber das hat mich fasziniert. Was war los?

Nach vielen Tests und einigem Googeln fand ich heraus, dass es möglicherweise mit den asynchronen Flanken des seriellen Eingangssignals zusammenhängt. Ich habe dann einen kaskadierten Latch eingefügt, um das Signal mit meiner internen Uhr zu synchronisieren, und alle Probleme waren verschwunden! Das ist eine schwierige Art zu lernen, dass Sie externe Signale immer synchronisieren müssen, bevor Sie sie in komplexe Logik einspeisen!

Ich muss sagen, dass das Debuggen und Verfeinern des Debuggercodes der schwierigste Teil dieses Projekts war. vor allem, weil es mit allen anderen Subsystemen einschließlich Bussen, dem Decoder und der Befehlswarteschlange interagiert.

Synthetisieren und Testen

Nach der vollständigen Kompilierung (ich habe Quartus II v9.1 sp2 verwendet) verbrauchte der FPz8-Kern 4.900 Logikelemente, 523 Register, 147.456 Bit On-Chip-Speicher und einen eingebetteten Neun-Bit-Multiplikator. Insgesamt nutzt das FPz8 80% der verfügbaren Ressourcen des EP4CE6. Das ist zwar viel, aber es stehen noch rund 1.200 Logikelemente für Peripheriegeräte zur Verfügung (mein einfacher 16-Bit-Timer summiert sich auf rund 120 Logikelemente und 61 Register). Es passt sogar auf den kleinsten Cyclone IV FPGA – den EP4CE6 – der auf dem kostengünstigen Mini-Board montiert ist, das ich hier verwendet habe (Abbildung 7).

ABBILDUNG 7. Altera Cyclone IV EP4CE6 mini bord.

Das Mini-Board verfügt (zusammen mit dem EP4CE6-Gerät) über einen seriellen EPCS4-Konfigurationsspeicher (auf der Unterseite montiert); einen FTDI-Seriell-zu-USB-Konverterchip sowie ein 50-MHz-Quarzoszillatormodul; einige Tasten; LEDs; und Stiftleisten für den Zugriff auf FPGA-Pins. Es gibt keinen integrierten USB-Blaster (für die FPGA-Programmierung), aber das Paket, das ich gekauft habe, enthielt auch einen externen Programmierdongle.

Was die realen Tests betrifft, so hat der FPz8 natürlich nicht beim ersten Mal funktioniert! Nachdem ich ein wenig nachgedacht und Compiler-Ausgabemeldungen gelesen hatte, stellte ich fest, dass es wahrscheinlich ein Timing-Problem war. Dies ist ein sehr häufiges Dilemma beim Entwerfen mit programmierbarer Logik, aber da dies mein zweites FPGA-Design war, habe ich ihm nicht genug Aufmerksamkeit geschenkt.

Beim Überprüfen der Timing-Analysemeldungen wurde eine Warnung angezeigt, dass der maximale Takt bei 24 MHz liegen sollte. Zuerst habe ich versucht, einen Divider-by-2 zu verwenden, um einen 25-MHz-CPU-Takt zu erzeugen, aber es war nicht zuverlässig. Ich habe dann einen Divider-by-3 verwendet. Alles begann perfekt zu funktionieren!

Deshalb läuft FPz8 derzeit mit 16.666 MHz. Es ist möglich, höhere Geschwindigkeiten zu erreichen, indem eine der internen PLLs verwendet wird, um den Haupttakt zu multiplizieren / zu dividieren, um einen resultierenden Takt zu erhalten, der niedriger als 24 MHz, aber höher als 16,666 MHz ist.

Programmieren und Debuggen

Die Bedienung des FPz8 ist sehr einfach und unkompliziert. Sobald das Design auf das FPGA heruntergeladen wurde, startet die CPU jedes im Speicher geladene Programm. Sie können eine Hex-Datei angeben und den MegaWizard-Plug-In-Manager verwenden, um die Programmspeicherinitialisierungsdatei zu ändern. Auf diese Weise wird Ihr Anwendungscode nach einem Reset-Signal ausgeführt.

Sie können die Zilog ZDS-II IDE verwenden, um Assembly- oder C-Code zu schreiben und die erforderlichen Hex-Dateien zu generieren (normalerweise wähle ich das Z8F1622 als Zielgerät aus, da es auch 2 KB RAM und 16 KB Programmspeicher hat). Dank des On-Chip-Debuggers ist es auch möglich, mit der ZDS-II-IDE Code über eine serielle Debug-Verbindung (in unserem Fall USB) auf das FPz8 herunterzuladen.

Stellen Sie vor dem Herstellen der Verbindung sicher, dass die Debuggereinstellungen mit denen in Abbildung 8 übereinstimmen. Deaktivieren Sie die Option „Seitenlöschung vor dem Flashen verwenden“ und wählen Sie „SerialSmartCable“ als aktuelles Debugging-Tool. Vergessen Sie nicht, auch zu überprüfen, ob der virtuelle COM-Port des FTDI korrekt als Debug-Port ausgewählt ist (verwenden Sie die Schaltfläche Setup). Sie können auch die gewünschte Kommunikationsgeschwindigkeit einstellen; 115,200 bps funktioniert sehr gut für mich.

ABBILDUNG 8. Debugger-Einstellungen.

Beachten Sie, dass die ZDS-II-IDE beim Anschließen an das FPz8 eine Warnmeldung anzeigt, die Sie darüber informiert, dass das Zielgerät nicht mit dem Projekt identisch ist. Das passiert, weil ich einige ID-Speicherbereiche nicht implementiert habe. Ignorieren Sie einfach die Warnung und fahren Sie mit der Debugsitzung fort.

Sobald der Code erfolgreich heruntergeladen wurde, können Sie die Anwendung starten (GO-Taste), Anweisungen ausführen, Register überprüfen oder bearbeiten, Haltepunkte setzen usw. Wie bei jedem anderen guten Debugger können Sie beispielsweise das PAOUT-Register (unter PORTS group) auswählen und sogar den Status der an PAOUT angeschlossenen LEDs ändern.

Einige einfache C-Code-Beispiele finden Sie in den Downloads.

Beachten Sie, dass das FPz8 über einen flüchtigen Programmspeicher verfügt. Somit geht jedes heruntergeladene Programm verloren, wenn das FPGA ausgeschaltet wird.

Dieses Projekt dauerte ein paar Wochen, aber es war herrlich, einen Mikrocontroller-Kern zu erforschen und zu entwerfen.

Ich hoffe, dass dieses Projekt für alle nützlich sein kann, die etwas über Computergrundlagen, Mikrocontroller, eingebettete Programmierung und / oder VHDL lernen möchten. Ich glaube, dass das FPz8 in Kombination mit einem kostengünstigen FPGA—Board ein fantastisches Lern- (und Lehr-) Werkzeug sein kann. Viel Vergnügen! NV

CEFET-PR:
www.utfpr.edu.br
ScTec:
www.sctec.com.br
HCS08 Entfesselt:
https://www.amazon.com/HCS08-Unleashed-Designers-Guide-Microcontrollers/dp/1419685929
Zilog eZ8 CPU Handbuch (UM0128):
www.zilog.com/docs/UM0128.pdf
Zilog Z8F64xx Product Specification (PS0199):
www.zilog.com/docs/z8encore/PS0199.pdf
Zilog ZDS II IDE User Manual (UM0130):
www.zilog.com/docs/devtools/UM0130.pdf
Zilog ZDS-II Software Download:
https://www.zilog.com/index.php?option=com_zcm&task=view&soft_id=7&Itemid=74
Zilog Microcontroller Product Line:
http://zilog.com/index.php?option=com_product&task=product&businessLine=1&id=2&parent_id=2&Itemid=56
Project Files available at:
https://github.com/fabiopjve/VHDL/tree/master/FPz8
FPz8 at Opencores.org:
http://opencores.org/project,fpz8

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.