Gesamtsystem
Das Gesamtsystem besteht aus insgesamt neun Entities, die sich grob in fünf übergeordnete Gruppen einsortieren lassen, wie in Abb. „Blockschaltbild des Gesamtsystems“ gezeigt.
Im Folgenden werden die einzelnen Entities näher beschrieben und deren Funktionsweise erläutert.

Clock_Divider
Die Clock_Divider-Entity dient zur Erzeugung aller benötigten Taktsignale, die in den verschiedenen Systembausteinen verwendet werden. Dafür wird, wie in Abb. „Flussdiagramm der Prozesse Clock_Unit_LCD“ gezeigt, ein Zähler initialisiert, dessen maximaler Zählerstand gleichzeitig die Frequenz des zu erzeugenden Taktsignals angibt. Bei jeder positiven Taktflanke des zugrundeliegenden 50 MHz-Signals, das auf dem Altera Entwicklungsboard DE2-115 zur Verfügung steht, wird der Zähler inkrementiert. Beim Erreichen des halben maximalen Zählerstandes wird ein STD_ULOGIC-Signal invertiert, wodurch ein symmetrisches Taktsignal erzeugt wird. Insgesamt werden durch diese Methode zwei Taktsignale erzeugt:
Ein 400 Hz-Signal, das für die Ansteuerung eines LCD verwendet wird und ein 12 MHz-Signal, welches der Ansteuerung des Audio-Codecs dient. [Wol05]

Quellcode - Clock_Divider
Wortgeschwindigkeit
Neben der bereits beschriebenen Variante zur Signalinvertierung für ein 400 Hz- und 12 MHz-Signal, wird ein Taktsignal zum Codieren und Decodieren eines Morsesignals benötigt. Da dafür allerdings verschiedene Morsegeschwindigkeiten eingesetzt werden können, muss das Taktsignal im laufenden Betrieb anpassbar sein. Als Auswahlmöglichkeit stehen dafür zwei Schalter auf dem Entwicklungsboard zur Verfügung, durch die das erzeugte Taktsignal auf eine Frequenz von 15 Hz, 39 Hz oder 60Hz eingestellt werden kann. Diese Geschwindigkeiten korrespondieren mit einer Morsegeschwindkeit von 5 Wpm (Wörter pro Minute), 13 Wpm und 20 Wpm.
Das hierbei erzeugte Signal wird ebenfalls durch einen Zähler realisiert, allerdings ist das erhaltene Signal asymmetrisch und wird nur für eine Periodendauer des 50 MHz-Signals aktiviert. [MR13]

Quellcode - Wortgeschwindigkeit
Morse / Decoder
Die Entity Morse stellt in diesem Projekt die Top-Level-Entity dar und verbindet die grundlegenden Komponenten des Systems miteinander. Neben der einfachen Verknüpfung wird in dieser Entität allerdings zusätzlich ein Tokensignal erstellt, dass in Abhängigkeit der Länge eines Tastendrucks des designierten Morsetasters entweder „01“ bei einer kurzen Betätigung dem Signal von rechts hinzufügt, oder „11“ bei einem langen Tastendruck. Wird der Taster allerdings länger nicht mehr betätigt, so wird das Tokensignal als vollständig erkannt, an weitere Systemkomponenten, wie z.B. die LCD-Entity, übergeben und anschließend zurückgesetzt, indem das Signal mit Nullen gefüllt wird. Durch die Unterscheidung von langen und kurzen Tastendrücken kann so ein Morsesignal decodiert werden. [MR13]
Zusätzlich zu der Decodierung wird ein einmaliger Softwarereset beim Einschalten des Systems durchgeführt, der dazu dienen soll alle Komponenten zu Beginn auf einen einheitlichen Betriebszustand zu setzen.

Quellcode - Morse/Decoder
Press_Duration
Die eigentliche Überprüfung ob es sich bei einem Tastendruck um eine kurze oder lange Betätigung handelt, findet in der Entity Press_Duration statt. Dafür wird bei einer positiven Taktflanke, des in der Entity Clock_Divider ermittelten Morsetaktes, das invertierte Signal des Morsetasters in ein elf Bit breites Signal von rechts hineingeschoben. Der Wert dieses Shiftsignals bestimmt daraufhin die Länge des Tastendrucks und übergibt das Ergebnis an die Entity Morse. Wurde der Taster für ein bis maximal drei Taktzyklen gedrückt, so handelt es sich hierbei um ein kurzes Signal. Bei einer Länge von fünf bis maximal neun Taktzyklen wird das Signal als ein langer Tastendruck registriert. Wird der Taster für zehn oder mehr Taktzyklen nicht betätigt, so handelt es sich hierbei um eine Pause und wird als solche gekennzeichnet. [MR13]

Quellcode - Press-Duration
Keyboard
Die Keyboard-Entity besteht aus drei Prozessen. Der erste Prozess „Sync“ dient allein der Synchronisation des PS/2-Taktsignals und des PS/2-Datensignals, die beide von der Tastatur gesendet werden, sobald eine Taste gedrückt oder losgelassen wird. Bei einer positiven Taktanke des internen 50MHz-Taktsignals werden die beiden eben erwähnten Signale in die neuen Signale „sync_keyclock“ und „sync_keydata“ überführt. Dadurch ist der aktuelle Status beider normalerweise zeitlich verschobenen Signale aneinander angepasst worden, was es ermöglicht, das übertragene und synchronisierte PS/2-Datensignal im Prozess „Key_Recognition“ zu identifizieren und den entsprechenden Hexcode der gedrückten Taste in das Signal „key_pressed“ zu schreiben.
Der dritte Prozess „Key_Data_Saving“ sorgt dafür, dass der ermittelte Hexcode erst dann an andere Systemkomponenten übergeben wird, wenn eine Taste auf der Tastatur nicht mehr gedrückt wird. Da die PS/2-Signale einen Makecode senden, sobald eine Taste gedrückt wird, wird ein Loslassen der Taste durch einen Breakcode quittiert. Dabei wird zunächst ein „F0“ über die Datenleitung des PS/2-Busses gesendet, bevor der eigentliche Hexcode übermittelt wird, der eine spezielle Taste identiziert. Um also zu ermitteln, ob eine Taste nicht mehr gedrückt wird, wird zunächst darauf gewartet, bis ein „F0“ in dem acht Bit breiten Signal „key_pressed“ gelesen werden kann. Anschließend wird ein Zähler inkrementiert, der immer zurückgesetzt wird, sobald das PS/2-Taktsignal auf LOW steht. Ist das Signal hingegen auf einem HIGH-Potenzial, so wird der Zähler so lange inkrementiert bis er einen Endstand erreicht, den er nur erreichen kann, wenn das PS/2-Taktsignal länger auf HIGH steht, als es zwischen einer negativen und positiven Flanke des kontinuierlichen Taktsignals des Make- oder Breakcodes der Fall ist. Wird der Zählerendstand erreicht, so bedeutet dies im Umkehrschluss, dass die Datenübermittlung abgeschlossen ist und der aktuell übermittelte Wert in „key_pressed“ auch gleichzeitig der Endwert der Datenübertragung ist und somit die zuletzt losgelassene Taste repräsentiert. Zusätzlich wird zu diesem Zeitpunkt das Signal „done_pressing“ auf ein logisches HIGH gesetzt und bedeutet dem restlichen System, dass eine Taste der Tastatur nun nicht mehr gedrückt wird und dass die Datenübertragung abgeschlossen wurde. [Lar18] [Ter13]

Quellcode - Keyboard IS


Quellcode - PS/2 shift in keyval
Keyboard_Encoder
In der Entity Keyboard_Encoder wird eine gedrückte Taste der PS/2-Tastatur in ein entsprechendes Morsesignal umgewandelt. Dafür muss ein Tokensignal generiert werden, das den entsprechenden Morsecode repräsentiert. Hierfür wird die gleiche Methode benutzt, wie beim Erstellen des Tokensignals in der Morse-Entity. Ein Dit (Punktsymbol des Morsecodes) wird dabei durch „01“ gekennzeichnet und ein Dah (Strichsymbol des Morsecodes) durch „11“. Insgesamt ist das Tokensignal 14-Bit breit um alle gängigen Morsezeichen (Buchstaben inkl. Umlaute, Zahlen und einige Sonderzeichen) abbilden zu können. Im Gegensatz zu dem Tokensignal in der Morse-Entity wird das Tokensignal hier jedoch umgekehrt beschrieben. In dem Morse-Token befindet sich an den beiden Bits mit der geringsten Wertigkeit das zuletzt eingegebene Morsesignal. Es befindet sich beispielsweise bei einem „A“ an dieser Stelle somit ein Dah, repräsentiert mit „11“, wohingegen sich in dem Tokensignal im Keyboard_Encoder ein Dit befinden würde, da ein „A“ durch ein Dit-Dah (.-) dargestellt wird. Die Generation des Tokensignals findet in dem Prozess „token_generation“ statt.
Ist das Tokensignal gesetzt, so wird im Prozess „audio“ das Tokensignal vom LSB zum MSB bitpaarweise überprüft. Handelt es sich bei dem Bitpaar um „01“, so wird das Signal „sound“ für zwei Taktperioden des entsprechenden Taktes der Wortgeschwindigkeit gesetzt. Handelt es sich bei dem Bitpaar hingegen um „11“, so wird das Signal für sechs Taktperioden gesetzt. Durch diese Unterscheidung wird ein Dah dreimal so lang wie ein Dit ausgegeben. Weist das Bitpaar allerdings „00“ auf, so ist die entsprechende Übertragung des Morsewortes beendet. Gleiches gilt, wenn der Zähler, der die Bitpaare einzeln abfragt, größer als 14 wird, da das Token-Signal zu diesem Zeitpunkt komplett analysiert wurde.
In der Entity Keyboard_Encoder wird allerdings zusätzlich der „Wortmodus“ eingeführt. Anstatt nur ein einzelnes Zeichen als Morsecode ausgeben zu können wird hier ein RAM-Speicher verwendet, der das aktuelle Tokensignal nach einer Tastenbetätigung in dem RAM speichert und die Speicheradresse anschließend inkrementiert. Wird im Anschluss an ein oder mehrere solcher Eingaben die „Enter“-Taste auf der Tastatur gedrückt oder die maximale Speicheradresse erreicht, so werden die gespeicherten Tokensignale nacheinander ausgelesen und wie zuvor beschrieben durch die Überprüfung der jeweiligen Bitpaare des Signals als Morsecode durch Audiosignale ausgegeben. Anschließend wird der Prozess „assignment“ aufgerufen, indem ein Softwarereset ausgeführt wird. Dabei wird der RAM komplett mit Nullen aufgefüllt, sodass die Audioausgabe nur einmal stattfinden kann.


Quellcode - Keyboard Encoder
Audioausgabe
Die Audioausgabe umfasst im Wesentlichen drei verschiedene Entities. Die Entity Audio_Codec bildet für dieses gesamte Teilsystem eine untergeordnete Top-Level-Entity, die alle benötigten Entities miteinander verbindet. Der grundsätzliche Aufbau der Audioausgabe beruht auf dem Quellcode der GitHub-Seite des Nutzers „AntonZero“ [Ant17]. Die einzelnen Teilkomponenten wurden entsprechend an die Anforderungen des Projektes angepasst.
Audio_Codec
Die Entity Audio_Codec übernimmt die komplette Steuerung der Tonerzeugung, sowie die Ansteuerung des integrierten Audio-Codecs mittels I²C-Signalen.
Das Audiosignal, das letztlich über den Line-Out-Ausgang des Entwicklerboards ausgegeben werden soll, wird zunächst aus einem ROM gelesen, der mithilfe eines „Memory Initialisation Files“ (.mif) beim Übertragen des Programms von Quartus II zum Entwicklerboard, so gefüllt wird, dass die gespeicherten Werte eine Periode eines 500Hz-Audiosignals repräsentieren. Im „read_audio_data“-Prozess wird dann bei gedrückter Morsetaste oder einem HIGH-Signal des erhaltenen „sound“-Signals der Entity Keyboard_Encoder, ein Datenwort aus dem ROM ausgelesen und die Speicheradresse des ROMs aus der das aktuelle Datenwort ausgelesen wird, inkrementiert und an die Entity Audio_Generation übergeben. [Ant17]
Weiterhin wird der Audio-Codec in dieser Entity auf einen bestimmten Betriebszustand eingestellt, was sich am Besten durch eine „Finite State Machine“ wie in Abbildung 4.12 dargestellt, beschreiben lässt. Dabei werden die einzelnen Betriebsmodi des Audio-Codecs nacheinander eingeschaltet, indem die entsprechenden Daten über ein I²C-Signal an den Audio-Codec gesendet werden. Wird ein Reset ausgelöst, so werden auch zunächst die Einstellungen des Codecs zurückgesetzt, bis der Reset nicht mehr durchgeführt wird. Anschließend wird der Codec erneut initialisiert. [Ter13] [Wol05]
Memory-Initialisation-File
Um die Datei zu erstellen, die den ROM mit Startwerten füllen kann, muss zunächst eine „.wav“-Datei produziert werden, die die gewünschten Audiodaten enthält. Dafür wurde mit dem Programm Audacity eine Datei erzeugt, die bei einer Samplerate von 48KHz genau eine Periode eines 500Hz Sinussignals darstellt. Anschließend wurde die Datei mit einem Hexeditor so bearbeitet, dass nur noch die Daten der Sinusschwingung vorhanden sind. Das bedeutet, dass alle Informationen, die sich auf die Metadaten der Audiodatei beziehen, wie z.B. die Länge des Liedes, aus der Datei entfernt wurden. Anschließend wurde mit einem MATLAB-Skript von der bereits zuvor erwähnten GitHub-Seite [Ant17] die übrig gebliebene Hexdatei (.hex) in ein Memory-Initialisation-File (.mif) umgewandelt, indem die Darstellung der Werte aus der Hexdatei in eine spezielle Tabellenform transformiert wird. [Ant17]
Im Platform Designer von Quartus II kann dann dem dort erstellten ROM-Baustein eine Speichergröße zugewiesen und anschließend die „.mif“-Datei zugeordnet werden.

Quellcode - Audio Codec IS

Quellcode - Einstellung Audio Codec durch StateMachine
I²C-Signal
Um Zeit und Ressourcen zu sparen wurde die I²C-Entity nicht eigenständig entworfen. Da ein „I²C“-Signal ein standardmäßiges Peripheriebusprotokoll ist, bestand keine Notwendigkeit eine neue Version einer bereits bekannten Systemkomponente zu verwirklichen und wurde deshalb von der GitHub-Seite des Nutzers AntonZero [Ant17] übernommen.
Quellcode - I2C-BUS
Audio_Generation
Die Entity Audio_Generation erhält als Eingangssignal das jeweilige Datenwort, das in der Entity Audio_Codec aus dem ROM ausgelesen wurde. Mit einem Zähler wird in dieser Entity das jeweilige Datenwort bitweise an den Audio-Codec seriell als Mono-Signal übermittelt. [Ant17]

Quellcode - Audio Generation
LC-Display
Das LC-Display wird mithilfe der LCD-Entity angesteuert. Mit einer „Finite State Machine“ wird hier das LCD auf einen entsprechenden Betriebsmodus eingestellt. Sollte ein Reset ausgelöst werden, so wird das Display erneut initialisiert. Im regulären Betrieb wird hier allerdings unterschieden ob das System gerade im „Decodermodus“ oder im „Encodermodus“ arbeitet.
Befindet sich das System im „Decodermodus“, so wird das erhaltene Tokensignal aus der Morse-Entity in ein entsprechendes Zeichen umgewandelt, was auf dem Bildschirm dargestellt werden kann.
Ist das System allerdings in den „Encodermodus“ versetzt worden, so wird der erhaltene Hexcode der Tastatur nach dem Loslassen einer Taste ebenfalls in ein Zeichen umgewandelt, welches auf dem Display darstellbar ist. Bei einer Umschaltung zwischen den beiden Modi wird die Anzeige des Displays geleert
um Verwirrungen zu vermeiden. [Hit98] [Lar18] [Ter13]
Quellcode - LC-Display