[PDF] Diplomarbeit. Implementierung und Test einer Emulationsplattform für die Hardware- Softwarepartitionierung eingebetteter Systeme. - Free Download PDF (2024)

Download Diplomarbeit. Implementierung und Test einer Emulationsplattform für die Hardware- Softwarepartitionierung eing...

Diplomarbeit

Implementierung und Test einer Emulationsplattform für die Hardware- Softwarepartitionierung eingebetteter Systeme

Carsten Nitsch

Universität Leipzig Institut für Informatik Prof. Dr. U. Kebschull

Forschungszentrum Informatik Sytementwurf in der Mikroelektronik Prof. Dr. W. Rosenstiel

Betreuer: Dipl.-Ing. K.Weiß

Hiermit versichere ich, daß ich die vorliegende Arbeit selbständig verfaßt und keine anderen als die aufgeführten Hilfsmittel verwendet habe.

Karlsruhe, den 23.05.1999

........................ Carsten Nitsch

3

Inhaltsverzeichnis Kapitel 1 Einleitung

13

1.1 Eingebettete Systeme ............................................................................................... 14 Kapitel 2 Motivation 2.1 Entwurfsprobleme .................................................................................................... 2.1.1 Eingebettete Systeme mit 4-Bit-Mikrocontrollern ....................................... 2.1.2 Eingebettete Systeme mit 8-Bit-Mikrocontrollern ....................................... 2.1.3 Eingebettete Systeme mit 16-Bit-Mikrocontrollern ..................................... 2.1.4 Eingebettete Systeme mit 32-Bit-Mikrocontrollern ..................................... 2.1.5 Fazit ..............................................................................................................

15 15 15 15 16 16 17

2.2 Inhalte dieser Arbeit ................................................................................................. 19 2.2.1 Erhöhung der Produktqualität ....................................................................... 19 2.2.2 Weiterentwicklung zur Forschungsplattform ................................................ 20 Kapitel 3 Grundlagen

21

3.1 Echtzeitbetriebsysteme ............................................................................................. 21 3.2 Programmierbare Logikbausteine ............................................................................ 3.2.1 Complex Programmable Logic Devices (CPLD) ......................................... 3.2.2 SRAM basierte FPGA .................................................................................. 3.2.2.1 Die XC-4000 Serie ......................................................................... 3.2.2.2 Virtex .............................................................................................. 3.2.3 Antifuse basierte FPGA (Actel) ....................................................................

24 24 26 26 33 35

3.3 Speicherbausteine ..................................................................................................... 37 3.3.1 EEPROMs ..................................................................................................... 37 3.4 Rekonfigurationsmethoden für FPGA ..................................................................... 39 3.4.1 Compile Time Rekonfiguration (CTR) ......................................................... 39 3.4.2 Run Time Reconfiguration (RTR) ................................................................ 39 Kapitel 4 Entwicklungsumgebung

43

4.1 Die Emulationsumgebung Spyder-Core-P1 ............................................................. 43 4.2 Entwicklungsumgebung SDS .................................................................................. 45 4.3 Echtzeitbetriebsystem RTEMS ................................................................................ 46 4.4 Echtzeitbetriebsystem VxWorks .............................................................................. 47 Kapitel 5 Implementierung

49

5.1 Diagnosesoftware ..................................................................................................... 49 5.1.1 Grundtest 1 .................................................................................................... 50

4

5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9

Grundtest 2 .................................................................................................... Grundtest 3 .................................................................................................... DRAM Test ................................................................................................... DRAM Test mit DPRAM-Interface .............................................................. Test des On-Chip-UART .............................................................................. Test der zweiten seriellen Schnittstelle ......................................................... Ethernet Interface .......................................................................................... Vollständiger Systemtest ...............................................................................

52 54 56 58 61 63 65 67

5.2 Bewertung des Laufzeitverhaltens eines eingebetteten Systems ............................. 5.2.1 Loader ........................................................................................................... 5.2.2 Meßumgebung .............................................................................................. 5.2.2.1 Benchmark für Interrupt Reaktionszeit .......................................... 5.2.2.2 Benchmark für Task Umschaltzeit ................................................. 5.2.3 Meßwerte ...................................................................................................... 5.2.4 Zusammenfassung ........................................................................................

69 70 72 72 74 76 77

5.3 Compile Time Rekonfiguration ............................................................................... 5.3.1 Funktionsprinzip ........................................................................................... 5.3.2 Programmieralgorithmus für ATMEL EEPROMs ....................................... 5.3.3 Programmierung von Atmel EEPROMS auf Spyder-Core .......................... 5.3.3.1 Dateiformate ................................................................................... 5.3.3.2 Programmierung .............................................................................

78 78 79 81 81 82

5.4 Run Time Rekonfiguration ...................................................................................... 5.4.1 DOS Filesystem für Flash-RAM .................................................................. 5.4.2 DOS Filesystem für DRAM (RAM-Drive) .................................................. 5.4.3 Gerätetreiber für XC4000 FPGA .................................................................. 5.4.3.1 Treiberintegration unter VxWorks .................................................. 5.4.3.2 Umsetzung des Konzepts für Spyder-Core-P1 ...............................

84 85 89 90 90 91

5.5 Demonstrator: Erweiterer FTP-Server ..................................................................... 94 5.5.1 Konzept ......................................................................................................... 94 5.5.2 Realisierung .................................................................................................. 95 Kapitel 6 Zusammenfassung und Ausblick

97

Anhang A Referenzen

99

Anhang B Loader

101

Anhang C Benchmarks

119

Messung der Interrupt-Umschaltzeit ................................................................................119 Messung der Task-Umschaltzeiten .................................................................................. 130

5

Anhang D Ladeprogramm für EEPROM

133

Anhang E Testprogramme

155

Grundtest 1 ...................................................................................................................... 155 Grundtest 2 ...................................................................................................................... 157 Grundtest 3 ...................................................................................................................... 162 DRAM Test ...................................................................................................................... 165 DRAM Test mit DPRAM-Interface ................................................................................ 170 Test des On Chip UARTs ................................................................................................. 178 Test der zweiten seriellen Schnittstelle ............................................................................ 186 Test der Ethernetschnittstelle ........................................................................................... 190 Anhang F Treiber

193

Treiber für Flash-RAM .................................................................................................... 193 Treiber für RAM Drive .................................................................................................... 200 Treiber für Xilinx FPGA XC-4000 .................................................................................. 202 Anhang G FTP Server

208

6

7

Abbildungsverzeichnis

Abbildung 2.1

Qualitativer Anstieg der Entwurfsproblematik bei eingebetteten Systemen (Quelle: [KW99])

....................................................................................17

Abbildung 3.1

Xilinx XC9500 CPLD-Architektur ([Xil98/1])

Abbildung 3.2

Xilinx XC4000 konfigurierbarer Logikblock (Quelle: [Xil98/1])

Abbildung 3.3

Xilinx XC4000 Dual-Port-SRAM Block (Quelle:[Xil98/1])

Abbildung 3.4

Xilinx XC4000 Ein- Ausgabeblock (Quelle: [Xil98/1])

Abbildung 3.5

High Level Routing Diagramm für XC4000 Serie (Quelle: [Xil98/1])

Abbildung 3.6

Switchmatrizen (Quelle: [Xil98/1])

........................................................32

Abbildung 3.7

Virtex Architektur (Quelle: [Xil99])

.......................................................33

Abbildung 3.8

2-Slice Virtex CLB (Quelle: [Xil99])

.....................................................34

Abbildung 3.9

ACT 1 Device (Quelle: [Act94])

Abbildung 3.10 ACT-1 Logikblock

......................................24 ..........27

.................28

.........................30 ..31

............................................................35

..................................................................................36

Abbildung 3.11 EEPROM Speichertransistor (Quelle: [HM93]) Abbildung 3.12 Compile Time Rekonfiguration([HuWi95]) Abbildung 3.13 Run Time Rekonfiguration ([HuWi95])

.....................................37

...........................................39

.................................................39

Abbildung 3.14 Globale Run-Time Rekonfiguration ([HuWi95])

...................................40

Abbildung 3.15 Lokale Run-Time Rekonfiguration ([HuWi95])

.....................................41

Abbildung 4.1

Blockschaltbild von Spyder-Core-P1 (Quelle: [KW99])

Abbildung 4.2

SDS Monitor Software

Abbildung 4.3

RTEMS Architektur (Quelle: [Army96])

Abbildung 4.4

Entwicklungsumgebung für VxWorks (Quelle: [KW99])

Abbildung 5.1

Busaktivitäten

Abbildung 5.2

Spyder-Core-P1 mit LEDs

Abbildung 5.3

Grundtest 3

Abbildung 5.4

Fehler auf Datenbus

Abbildung 5.5

Dual Port RAM Interface

Abbildung 5.6

Speicherauszug durch DOS-Programm Debug

Abbildung 5.7

Schnittstellentest (Pseudocode)

Abbildung 5.8

Loop-Back-Beschaltung einer 9-PIN Sub-D Buchse

........................44

............................................................................45 ................................................46 ......................47

.........................................................................................52 ......................................................................54

..............................................................................................55 ................................................................................57 ........................................................................58 ......................................60

..............................................................61 .............................62

8

Abbildung 5.9

Externe serielle Schnittstelle als Bestandteil der I/O Bank

....................63

Abbildung 5.10 Loop-Back Beschaltung für den Test der externen seriellen Schnittstelle

.

64 Abbildung 5.11 MB86960 ([Fu97])

..................................................................................65

Abbildung 5.12 Testalgorithmus für den Test des Ethernet Interfaces Abbildung 5.13 Nutzung des Dual-Port-RAM durch den Loader Abbildung 5.14 Ermitteln der Interrupt Reaktionszeit

Abbildung 5.17 Messen der Tasku*mschaltzeiten 2

......................................73

..............................................................74 ...........................................................75

Abbildung 5.18 Datenfluß beim Laden des seriellen EEPROM Abbildung 5.19 Bitformate

....................................70

......................................................72

Abbildung 5.15 Cachekonfiguration im Betriebsystem RTEMS Abbildung 5.16 Messen der Tasku*mschaltzeiten

.............................66

......................................78

...............................................................................................79

Abbildung 5.20 Messageformat für die Programmierung eines AT17Cx EEPROM (Quelle: [Atmel1])

.................................................................................................80

Abbildung 5.21 Device Address Byte (Quelle: [Atmel1]) Abbildung 5.22 Address Bytes (Quelle: [Atmel1]) Abbildung 5.23 Xilinx Bitstream Format Abbildung 5.24 Intel MCS Format

...............................................80

..........................................................81

.........................................................................81

...................................................................................82

Abbildung 5.25 Programmierung eines EEPROM auf Spyder-Core-P1 Abbildung 5.26 Run Time Rekonfiguration auf Spyder-Core-P1

..........................82

....................................84

Abbildung 5.27 Non-Block Devices vs. Block Devices (Quelle:[Wind97]) Abbildung 5.28 Flash-RAM als Laufwerk

....................85

.......................................................................86

Abbildung 5.29 Erstellen einer Parametertabelle für Block Device Treiber Abbildung 5.30 Formatieren und automatisch mounten

....................87

...................................................87

Abbildung 5.31 Mounten eines existierenden Filesystems

...............................................88

Abbildung 5.32 Erzeugen und Mounten einer RAM-Disk

...............................................89

Abbildung 5.33 Treiberinitialisierung für Non-Block Devices (Quelle:[Wind97]) Abbildung 5.34 Hinzufügen von Geräten (Quelle:[Wind97])

..........................................91

Abbildung 5.35 Hardware-Initialisierungssequenz der Funktion Open() Abbildung 5.36 Datenübertragung

.........90

.........................92

....................................................................................92

9

Abbildung 5.37 Integration des Spyder-Core Systems in die Benutzeroberfläche des Hostrechners

...........................................................................................94

Abbildung 5.38 Assoziation zwischen Verzeichnissen und Gerätetreibern Abbildung 6.1

......................95

Performance von Spyder Core P1 bei der Rekonfiguration des FPGA

..98

10

11

Tabellenverzeichnis

Klassische versus Echtzeitsysteme ([MS97]) .................................................................... 22 Look Up Table ................................................................................................................... 27 Unterstützte RAM Modes (Quelle:[Xil98/1]) .................................................................... 29 Fetching .............................................................................................................................. 51 Dual Port ROM Interface: Adressen .................................................................................. 59 Parameter für Schnittstellentest (On-Chip-UART) ............................................................ 61 Parameter für Schnittstellentest (externer UART) ............................................................. 63 Konfiguration des MB86960 .............................................................................................. 65 Tasku*mschaltzeiten für PPC403 GA (40 Mhz) .................................................................. 76 Tasku*mschaltzeiten für PPC 403GCX (40 Mhz) ............................................................... 76 Interruptreaktionszeiten für PPC 403GA (40 Mhz) ........................................................... 76 Interruptreaktionszeiten für PPC 403GCX (40 Mhz) ........................................................ 76 Block Device Parameter unter VxWorks ........................................................................... 86 Parameter für RAM-Drive ................................................................................................. 89 Signale und Adresse für Spyder-Core-P1 .......................................................................... 93

12

13

Kapitel 1

Einleitung Vor über 40 Jahren galten Computer noch als etwas Besonders. Riesige Rechenmonster wie die UNIVAC füllten ganze Häuser mit lärmenden Lochkartenlesern, Relais, Fernschreibern und ähnlichem Equipment. Wer zu dieser Zeit eine Rechenmaschine betrieb, brauchte viel Platz und eine leistungsfähige Energieversorgung. Kaum jemand glaubte ersthaft daran, daß die Welt mehr als eine wenige dieser Wunderwerke benötigen wird. Heute wissen wird, daß diese Prognose wahrscheinlich der größte Irrtum der Zukunftsforschung war. Computer sind aus unserem Alltag nicht mehr wegzudenken. Sie sind in der Arbeitswelt wie auch in Privathaushalten weit verbreitet. Ein stetiger Preisverfall sorgt dafür, daß die Rechentechnik zum Alltagsgut wird. Nach dem Gesetz von Moore verdoppelt sich die Anzahl der Transistoren pro Chip aller 18 Monate. Momentan sind Taktfrequenzen von 400-600 Mhz üblich. In den Labors der Chiphersteller laufen bereits Prototypen mit einem Takt von 1 GHz. Die Leistungsfähigkeit der Computer steigt immer stärker und damit auch die Anzahl der potentiellen Einsatzgebiete. Im Gegensatz zu Desktopcomputern verlief die größte technische Revolution unbemerkt. Schauen wir uns einmal in unserer Wohnung um. Wieviele Mikroprozessoren besitzen wir? Sind es fünf, zehn oder etwa doch viel mehr? Den wenigsten Menschen ist bewußt, daß in Videorecordern, ISDN-Anlagen, Mobiltelefonen, Küchengeräten, Stereoanlagen, Waschmaschinen und Autos Computer „versteckt“ sind. Selbst auf Smartcards verrichten diese Systeme unauffällig ihren Dienst. Diese Systeme müssen oft ähnlich komplexe Aufgaben lösen wie Desktopcomputer, unterliegen im Gegensatz zu diesen jedoch weitaus strengeren Restriktionen in Bezug auf Platzbedarf und Kosten. Daher werden im Bereich der integrierten Funktionseinheiten die für den Betrieb unbedingt benötigten Funktionseinheiten auf einem Chip integriert, z.B. serielle Schnittstellen, ISDN Interfaces oder Speicherkontroller.

14

Einleitung

1.1 Eingebettete Systeme In der Fachliteratur hat sich für diese integrierten Systeme der Begriff eingebettetes System etabliert. Es existieren Definitionen für eingebettete Systeme, wie z.B. in [Wo94] beschrieben. Eine allgemein akzeptierte Definition hat sich aber jedoch noch nicht durchgesetzt. Ich möchte das Definitionsproblem an einem Beispiel veranschaulichen: • Ein handelsüblicher PC wird mit einem Touch Screen und einem Thermodrucker ausgestattet und in einem stabilen Stahlgehäuse eingeschlossen. Dieses System dient dann z.B. als Informationsterminal auf einem Bahnhof, mit dessen Hilfe man Fahrpläne und Zugverbindungen abfragen kann. Man kann dieses System in den Bereich der allgemeinen Rechentechnik einordnen, immerhin verrichtet Standardhardware die notwendige Arbeit. Andererseits spricht nichts dagegen, das Informationsterminal als eingebettetes System zu betrachten, da es sich um spezialisierte Hardware handelt. • Es gibt spezielle Smartcards, welche z.B. in Hotels eingesetzt werden. Mit Hilfe dieser Smartcards wird eine Zugangskontrolle realisiert, sie dienen zum Bezahlen, Telefonieren usw. Einige haben sogar ein integriertes Display. Dieses Smartcards könnte man sowohl als eingebettetes System als auch als Application Specific Integrated Processor (ASIP) betrachten, da alle Funktionen in einem Chip realiisert sind.. Ein wesentliches Kriterium von eingebetteten Systemen ist, daß sie für spezielle Anwendungen entwickelt wurden. Auf den Systemen wird man einen Mikrocontroller finden. Auf diesen Mikrocontrollern läuft eine spezielle Software, welche einen bestimmten Teil der Systemfunktionen erfüllt. Funktionen, die sich nicht in Software realisieren lassen, erfordern die Bereitstellung bestimmter Hardware. In [KW99] wird ein eingebettetes System wie folgt definiert: Definition 1.1: Ein eingebettetes System besteht aus einem anwendungsspezifischen Softwareteil, der auf einem Mikrocontroller abgearbeitet wird und aus einem anwendungsspezifischen Hardwareteil. Beide Teile kommunizieren untereinander und mit der Umgebung. Insbesondere die Umgebung gibt bestimmte Randbedingungen vor, die von dem eingebetteten System eingehalten werden müssen.

15

Kapitel 2

Motivation 2.1 Entwurfsprobleme In eingebetteten Systemen findet man Mikrocontrollerarchitekturen verschiedener Komplexität.

2.1.1 Eingebettete Systeme mit 4-Bit-Mikrocontrollern 4 Bit Mikrocontroller sind sehr einfache Architekturen. Ihre Leistungsfähigkeit ist begrenzt, dafür sind sie extrem billig. Die geringe Komplexität erlaubt die Programmierung in Assemblersprache. Die Programmierung erfolgt auf einem PC. Der entstehende ausführbare Maschinencode ist vom Umfang sehr klein und wird in sogenannten EPROMS abgelegt. 4 Bit Mikrocontroller finden sich überall dort, wo einfache Aufgaben zur Systemsteuerung zu erfüllen sind, z.B. in Waschmaschinen. Hier ist der Einsatz der 4 bit Controller wesentlich preiswerter und robuster als die alternative Verwendung einer mechanischen Steuerung.

2.1.2 Eingebettete Systeme mit 8-Bit-Mikrocontrollern Diese Architektur ist zur Zeit am weitesten im Embedded Bereich vertreten. Man findet sie sowohl im Consumerbereich als auch in sicherheitskritischen Bereichen der Luft- und Raumfahrt. Viele dieser Architekturen stammen aus dem PC und Workstationbereich, z.B. der Z80. Es existieren zahlreiche Derivate. Die heute eingesetzten Prozessoren verfügen gegenüber den ursprünglichen Versionen zusätzlich über eine umfangreiche Peripherie, welche auf dem Chip integriert ist. Dazu zählen Ein- und Ausgabeeinheiten wie UARTS1, aber auch Speicher, Timer, 1. Serielle Schnittstellen

16

Motivation

Interrruptcontroller und vieles mehr. Die Fortschritte der Fertigungstechnologien für integrierte Schaltungen erlauben außerdem, die Bausteine mit wesentlich höheren Taktraten zu betreiben. Die CPUs bleiben dabei gegenüber ihren Vorgängern registerkompatibel. Die lange Verfügbarkeit ermöglicht eine extreme Zuverlässigkeit der Systeme mit diesen Mikroprozessoren. 8 Bit Systeme sind etwas komplexer als die 4 Bit Architekturen. Ein solches System läßt sich immer noch relativ leicht in reinem Assembler programmieren, es existieren jedoch auch Compiler für höheren Programmiersprachen. Diese Compiler laufen auf einem PC und generieren Maschinencode, welcher auf dem 8 bit System lauffähig ist. Die Architektur der Mikrocontroller ist einfach, ebenso die Initialisierung der Peripherie. Wie bei früheren Homecomputern kann eine Anwendung die gesamte Hardware selbst steuern. Dies ermöglicht eine sehr effiziente Programmierung. Die simple Systemarchitektur und die exklusive Programmierung der Hardware durch die Applikation erlauben Laufzeitvorhersagen durch Simulatoren.

2.1.3 Eingebettete Systeme mit 16-Bit-Mikrocontrollern Diese Architekturen sind den 8 bit Controllern ähnlich. Sie besitzen jedoch breitere Register und Busse. Gegenüber den 8bit Mikrocontrollern nimmt die Komplexität der peripheren Bausteine zu. Während bei den 8bit Systemen standardisierte Peripherie geinger Komplexität dominiert, finden sich hier zunehmend sogenannte ASICs. ASICs sind anwendungsspezifische integrierte Schaltkreise. Diese entlasten die CPU durch intelligente Eigenfunktion und ermöglichen somit eine komplexere Ein- und Ausgabefunktionalität (z.B. Ethernet). Problematisch ist jedoch die unter Umständen sehr komplexe Initialisierung dieser Chip, welche durch den Mikrocontroller erfolgt. Die Softwareentwicklung wird dadurch komplizierter und fehleranfällig.

2.1.4 Eingebettete Systeme mit 32-Bit-Mikrocontrollern Moderne 32 bit Prozessoren verfügen in der Regel über Daten und Befehlscaches. Diese Caches minimieren Zugriffe auf externe Speicher. Dadurch werden unnötige Wartezyklen vermieden und die Performance des Systems gesteigert. Weitere Features sind Pipelinestrukturen, parallele Ausführeinheiten, Superskalarität und „Out-of-order-Execution“. Eine nähere Erklärung dieser Begriffe findet sich in [HePa96]. Diese Eigenschaften führen zu einer sehr starken Erhöhung der Rechenleistung. Leider verkompliziert sich durch diese Konzepte aber auch die Programmierung. Der Maschinencode wird von der CPU nicht mehr in der Reihenfolge abgearbeitet, wie er im Assemblerprogramm notiert ist, Speicherzugriffe bewirken nur unter bestimmten Umständen einen Buszugriff usw. Die Komplexität der Peripherie steigt gegenüber den 16-Bit-Systemen nochmals stark an. Die Entwicklung von Software in reinem Assembler wird sehr kompliziert bis praktisch unmöglich. Auch die Programmierung mit Hochsprachen bleibt anspruchsvoll, wenn eine Applikation das gesamte Hardware-Handling selbst übernehmen muß. Eine weiteres Problemfeld entsteht durch die hohe Integrationsdichte moderner Chips. Integrierte Schaltkreise besitzen oft mehrere hundert Pins. Es ist nicht mehr möglich, Prototypen

17

im Labor „von Hand“ zu bauen. Vielmehr ist es von Anfang an erforderlich, gedruckte Platinen mit mehreren Lagen zu verwenden. Die Herstellung dieser Platinen ist entsprechend teuer. Funktioniert ein solcher Prototyp nicht, ist die Fehlersuche kompliziert. Bauformen wie SMD1 und Ballgrid-Arrays (bis 600 Pins !) erschweren oder versperren Meßgeräten wie Logic Analyzern den Zugriff auf das System.

2.1.5 Fazit Wie Abbildung 2.1 [KW99] zeigt, steigen die Entwurfsprobleme bei 32-Bit-Systemen überproportional an. Die Entwicklung von Anwendungen wird sehr teuer, wenn dem Programmierer sehr tiefgreifende Systemkenntnisse abverlangt werden (Schulung, Zeitaufwand). Das Problem verschärft sich, wenn Anwendungen entwickelt werden, welche auf verschiedenen eingebetteten Systemen laufen sollen.

Überlagernde Entwurfsprobleme

Einsatz von Betriebssystemen, starke Echtzeitanforderungen, Mikrocontroller mit dynamischem Verhalten (Caches, Sprungvorhersagen) massive technologische und elektrische Anforderungen hochintegrierte VLSI-ASICs mit komplexer Initialisierung

Komplexere Peripherie, erhöhte technologische Anforderungen Erweiterung des Einsatzspektrums, Echtzeitanforderungen

Übergang zu C-Code Speicher- und Geschwindigkeitprobleme nur einfache logische Operationen Assembler

8 16 4 32 Systemkomplexität = f(Bitbreite der Mikrocontroller-Architektur) Abbildung 2.1 Qualitativer Anstieg der Entwurfsproblematik bei eingebetteten Systemen (Quelle: [KW99])

Um dennoch eine effiziente Softwareentwicklung zu ermöglichen, ist es erforderlich, die Hardwarestruktur vor den Anwendungsprogrammen zu verbergen. Dies ist Aufgabe von Be-

1. Surface Mounted Device

18

Motivation

triebssystemen. Betriebssysteme für eingebettete Systeme stellen im wesentlichen eine Bibliothek von Funktionen dar. Diese Funktionen bilden eine Schnittstelle zwischen der Applikation und der Hardware. Die Applikation kann dadurch auf einem abstrakteren Niveau auf das System zugreifen. So ist es z.B. möglich, auf Flash-Speichern, Floppydisks oder auch SCSI-Festplatten auf die gleiche Art und Weise zuzugreifen, indem ein Filesystem benutzt wird. Alle Zugriffe auf dieses Filesystem werden vom Betriebssystem auf spezielle Hardwareoperationen abgebildet. Dies geschieht für die Anwendung völlig transparent. Desweiteren entlasten Betriebssystemroutinen den Anwendungsprogrammierer von aufwendigen Verwaltungsaufgaben. So bieten Betriebssysteme wie VxWorks auch für eingebettete Systeme einen TCP/IP Stack, so daß die Kommunikation über das Internet ermöglicht wird. [Wind97] Für den Hardwareentwickler ist es essentiell, Komponenten bereits vor der Fertigung testen zu können. So benutzen ASIC Entwickler programmierbare Logbausteine, sogenannte FPGAs 1. Die Logik des Chips wird in einer Sprache wie VHDL 2 beschrieben und auf logische Funktionalität getestet. Dannach wird durch ein Synthesetool ein spezielles Bitfile erstellt. Dieses wird auf das FPGA übertragen, wodurch dieses programmiert wird. Der Baustein kann auf seine Funktionalität getestet werden. Wird ein Fehler im Design entdeckt, wird dieser korrigiert und das FPGA neu programmiert. Erst wenn die integrierte Schaltung fehlerfrei arbeitet, wird sie industriell auf billigeren, nicht programmierbaren Chips gefertigt.

1. Field Programmable Array 2. (Very High Speed Integrated Circuits) Hardware Description Language

19

2.2 Inhalte dieser Arbeit Für die schnelle und kostengünstige Entwicklung eines eingebetteten System sind also zwei wesentliche Komponenten erforderlich: • Ein System mit Mikroprozessor, auf welchem ein Echtzeitbetriebssystem bereits lauffähig ist. Dieses System dient dazu, die Applikation testen zu können, ohne daß die eigentliche Zielhardware benutzt werden muß. Das Emulationssystem gestattet eine bessere Testbarkeit, indem z.B. Schnittstellen wie Ethernet für die Kommunikation mit dem Entwicklungs-PC vorgesehen sind. Weiterhin ist es möglich, Software zu entwikkeln, während die Zielhardware noch nicht fertiggestellt ist. Die Entwicklungszeit verringert sich, was einen entscheidenten Wettbewerbsvorteil bedeutet. • Eine Umgebung, welche die vollständige Funktionsprüfung eines ASIC-Prototypen gestattet, der als FPGA vorliegt. Vollständige Funktionsprüfung erfordert Meßbarkeit aller Signale. Desweiteren muß die Möglichkeit bestehen, entsprechende Testmuster an den Chip zu senden, um seine Funktion zu testen. Die Programmierung des FPGA sollte schnell und einfach sein, d.h. die Rekonfigurierung muß erfolgen können, während das FPGA auf dem Board verbleibt. Ziel ist, Hardware so einfach wie Software abändern zu können. Das Emulationssystem SPYDER-CORE-P1 vereint beide Komponenten in einem System. Eine genauere Beschreibung der Plattform findet sich in einem späteren Kapitel. Inhalt dieser Arbeit war, durch die Implementierung geeigneter Softwarekomponenten die Leistungsfähigkeit des Spyder-Systems deutlich zu erhöhen und sein Einsatzfeld zu erweitern. Ich möchte dabei zwei wesentliche Punkte herausstellen:

2.2.1 Erhöhung der Produktqualität Spyder-Core wurde von Angang an mit der Zielstellung entwickelt, einen Platz als innovatives Entwicklungswerkzeug in den Entwicklungslabors der Industrie zu finden. Um dieses Konzept erfolgreich umsetzen zu können, ist eine ständige Verbesserung des Produktwertes von essentieller Bedeutung. Für den Kunden sind dabei vor allem Faktoren wie Zuverlässigkeit sowie Benutzerfreundlichkeit wesentliche Entscheidungskriterien. Um diesen Ansprüchen gerecht werden zu können, habe ich das Spyder-System um folgende Komponenten erweitert: • Test- und Diagnosepaket: Dieses Paket besteht aus einer Reihe von Testroutinen. Mit Hilfe dieser Routinen ist es möglich, die korrekte Funktionalität des Systems systematisch zu überprüfen, bevor es an einen Kunden ausgeliefert wird. Die Konzeption dieses Paketes beeinhaltet im wesentlichen, den Bereich der Qualitätssicherung für das Spyder-Core System abzudecken. • Benchmarkprogramme: Ein Entwickler eingebetteter Systeme muß in der Lage sein, die Echtzeitfähigkeit seines Produktes zu garantieren. Dazu sind insbesondere quantitative Aussagen darüber erforderlich, wie das System auf externe Ereignisse wie z.B Interrupts reagiert. Im Verlauf dieser Arbeit habe ich die für das Betriebssystem VxWorks existierenden Benchmarkprogramme verifiziert. • Entwicklungstool für Compile Time Rekonfiguration: Dieses Werkzeug erlaubt die komfortable Programmierung eines elektrisch löschbaren EPROMS vom Entwick-

20

Motivation

lungs-PC aus, wobei der Chip auf dem Board verbleiben kann. Dem Anwender wird durch dieses neue Feature die zeitraubende und fehlerträchtige Programmierung der Speicherbausteine mittels eines externen Programmiergerätes erspart, wodurch sich der Produktivitätsgewinn durch den Einsatz von SPYDER-CORE-P1 weiter erhöht.

2.2.2 Weiterentwicklung zur Forschungsplattform Die Kerngedanke der Arbeit am Forschungszentrum Informatik1 ist es, innovative Ergebnisse der wissenschaftlichen Forschung in kurzer Zeit nutzbar zu machen. Dies erfordert neben einer praxisorientiert Umsetzung von Forschungsergebnissen natürlich auch die ständige Erforschung wissenschaftlichen Neulandes. Bisher betand die Aufgabe des Spyder-Core System vor allem darin, als leistungsfähiges Werkzeug die Effizienz der Arbeit in den Entwicklerlabors zu erhöhen. Da die Architektur von SPYDER-CORE-P1 die Programmierung eines FPGA durch den Mikroprozessor gestattet, eröffnet sich für dieses System nun ein neues interessantes Gebiet, welches bisher noch Gegenstand der Forschung ist. Man stelle sich vor, daß man ein System entwickeln muß, welches eine sehr komplexe Hardware benötigt. Das Design ist dann so umfangreich, daß unter Umständen mehrere teure FPGA benötigt werden, um es zu implementieren. Teure Eingebettete Systeme sind aber „schlechte“ Systeme. Dies gilt jedenfalls dann, wenn man ein solches System kommerziell vertreiben will. Wie kann man eine Maschine realisieren, welche das vorgegebene Pflichtenheft erfüllt und trotzdem einen geringen Hardwareaufwand benötigt? Eine Möglichkeit eröffnet sich, wenn folgende Vorraussetzung erfüllt ist: Unter Umständen werden nie alle Komponenten der Hardware gleichzeitig benötigt. Es macht also keinen Sinn, das vollständige Hardwaredesign zur Verfügung zu stellen. Das System sollte sich die zu einem Zeitpunkt benötigte Hardware einfach generieren und anschließend wieder verwerfen. Dies Konzept erscheint auf den ersten Blick nicht realisierbar, schließlich kann man Hardware nicht einfach erzeugen und wieder verschwinden lassen. Mit Hilfe von FPGAs ist die Idee jedoch zu verwirklichen. Durch die Programmierbarkeit dieser Bausteine reicht es aus, die Design Files auf einem geeigneten Medium zu speichern und bei Bedarf in den programmierbaren Logikchip zu laden. Dieser verhält sich danach wie ein „klassischer“ ASIC. Diese Hardwareprogrammierung „nach Bedarf“ nennt man Run-Time-Rekonfiguration (RTR). Ein späterer Abschnitt widmet sich ausführlicher den Möglichkeiten, welche sich durch die Anwendung von RTR ergeben. Um das Spyder-Core-System als Forschungsplattform für Run-Time-Rekonfiguration einsetzen zu können, mußte es um einige weitere Features erweitert werden: • Bereitstellung eines Filesystems zur lokalen Speicherung der Design Files • Implementierung einer Schnittstelle, welche einer Applikation die einfache Rekonfiguration eines FPGA ermöglicht

1. Das Forschungszentrum Informatik (FZI) in Karlsruhe ist ein Partnerinstitut dr Universität Leipzig. Die Ergebnisse dieser Arbeit enstanden im Rahmen meiner (externen) Diplomarbeit am FZI.

21

Kapitel 3

Grundlagen In diesem Kapitel möchte ich in einige prinzipielle Grundlagen bezüglich der Hard- und Softwarekomponenten einführen, welche für das weitere Verständnis dieser Arbeit notwendig bzw. hilfreich sind. Dieses Kapitel erhebt keinen Anspruch auf Vollständigkeit. Der interessierte Leser sei also auf die weiterführende Literatur verwiesen. So bauen die Ausführungen über die Hardwarekomponenten im wesentlichen auf die im Referenzteil gelisteten Manuals und Data Sheets der Hersteller sowie [KW99] auf, so daß ich diese Quellen für ein ausführlicheres Studium empfehle.

3.1 Echtzeitbetriebsysteme Die Software eines eingebetteten Systems kommuniziert über Peripheriegeräte mit der Außenwelt. Sie registriert Ereignisse, analysiert diese und führt daraufhin entsprechende Aktionen aus. Es liegt in der Natur vieler Ereignisse, daß diese asynchron auftreten. So kann z.B. ein Sensor in einer Fertigungsstraße zu einem beliebigen Zeitpunkt ein Signal an das System senden, worauf dieses reagieren muß. In der Praxis unterliegt die Zeitspanne zwischen dem Auftreten des Signals und der Reaktion des eingebetteten Systems oft harten Zeitrestriktionen. Damit diese Restriktionen eingehalten werden können, muß das Verhalten des Systems vorhersagbar sein. Für das Betriebssystem bedeutet dies, daß Reaktionszeiten auf Ereignisse garantiert werden müssen. Bei klassischen Betriebssystemen ist dies nicht der Fall. Hier kann es durchaus vorkommen, daß ein gerade laufender Prozeß durch einen anderen verdrängt und seine Abarbeitungszeit dadurch extrem verlängert wird. Das System reagiert auf ein externes Ereignis dann viel zu spät, was zu katastrophalen Folgen führen kann. Definition 3.1: Echtzeitbetrieb ist ein Betrieb eines Rechensystems, bei dem Programme zur Verarbeitung anfallender Daten ständig betriebsbereit sind derart, daß die Verarbeitungsergebnisse innerhalb einer vorgegebenen Zeitspanne verfügbar sind. Die Daten können je nach Anwendungsfall nach einer zeitlich zufälligen Verteilung oder zu vorbestimmten Zeitpunkten anfallen. (DIN 44300)

22

Grundlagen

Definition 3.2: Real-Time systems are those systems in which the correctness of the system depends not only on the logical result of computation, but also on the time at which the results are produced.1 [St90] Echtzeitanforderungen lassen sich in „hard real time“ und „soft real time“ einteilen. Während bei harten Echtzeitbedingungen das Ergebnis durch eine Überschreitung der Antwortzeit vollkommen unbrauchbar wird, ist bei Systemen mit „weichen“ Echtzeitanforderungen solch eine Verletzung für einen Teil der Ergebnisse noch tolerierbar. Im weiteren Verlauf meiner Ausführungen werde ich mich auf die Diskussion harter Echtzeitbedingungen beschränken. Die folgende Tabelle macht deutlich, durch welche Eigenschaften sich Echtzeitbetriebssysteme gegenüber klassischen Betriebssystemen auszeichnen, um den Anforderungen gerecht zu werden [MS97]

Klassisches System

Echtzeitsystem

hohe Antwortzeiten

Latenzzeiten gering (typ. 20 µs)

hohe Schedulingzeiten (ab 10 ms)

Scheduling im Mikrosekundenbereich (30-200 µs)

lange Interruptsperren

spezielle, schnelle Interruptbehandlung

nichtunterbrechbare Kernelaufrufe

teilweise preemptive Kernel

gleichmäßige Rechenzeitaufteilung

Prioritätsklassen

Annahme jedes Prozesses

Last- und Möglichkeitsanalyse

Tabelle 3.1: Klassische versus Echtzeitsysteme ([MS97]) Ein Echtzeitbetriebssystem zeichnet sich also keinesfalls nur dadurch aus, besonders „schnell“ zu sein, da eine Minimierung der mittleren Antwortzeiten nicht ausreichend ist. Um ein Echtzeitbetriebssystem zu implementieren besteht zum einen die Möglichkeit, ein Unixsystem um einen preemptiven Microkernel zu erweitern. Dies ermöglicht die Erfüllung harter Echtzeitbedingungen. Der Codeumfang der Routinen läßt sich klein halten, so daß diese Systeme für den Embedded-Bereich mit seinen teils beschränkten Speicherresourcen geeignet sind. Die Implemetierungsanforderungen sind jedoch relativ hoch, da aufwendige Lock- und Synchronisationsverfahren realisiert werden müssen. Vertreter dieser Systeme sind QNX von QNX Software, Chorus (Chorus) sowie VxWorks, welches von der Firma WindRiver Systems vertrieben wird. Eine nähere Betrachtung zu VxWorks ist im folgenden Kapitel zu finden. Eine andere Variante der Implementierung besteht darin, einen Real-Time-Scheduler zu entwerfen. Dieser läßt dann den eigentlichen Betriebssystemkernel als Idle-Task laufen. Diese Lösung führt zu einem sehr schnellen System, auf welchem Standardanwendungen weiterhin lauffähig sind. Leider sind solche Systeme nur für synchrone Echtzeitanforderungen geeignet. Ein geplantes Scheduling ist ebenfalls nicht möglich. Vertreter sind EP/LX auf Lynx OS (Control Data) sowie Linux mit Realtime Patch.

1. „Echtzeitsysteme sind solche Systeme, in welchen die korrekte Funktion des Systems nicht nur von den logischen Ergebnissen der Berechnungen abhängt, sondern auch von der Zeitspanne, in der diese erzeugt werden“

23

Das Scheduling erfolgt bei Echtzeitbetriebssystemen nach Klassen. Ein Hardware Dispatcher verwaltet Interrupts, während der Software Dispatcher den einzelnen Prozessen bzw. Threads1 Rechenzeit zuteilt: • Realtime Gruppe für deterministisches Echtzeitverhalten. Das Scheduling erfolgt streng auf der Basis von Prioritäten und Zeitscheibe • System Gruppe für System Threads: Die Prioritäten für Vertreter dieser Gruppe sind bereits beim Start des Betriebssystems fest vorgegeben. Darunter fallen z.B. Swaper und Pager. • Time Sharing Gruppe: Alle unkritischen Prozesse laufen im Timesharing mit einer Zeitscheibe von 10..100 ms.

1. Die Begriffe Thread und Prozeß werden im Rahmen dieser Abhandlung als Synonyme behandelt

24

Grundlagen

3.2 Programmierbare Logikbausteine 3.2.1 Complex Programmable Logic Devices (CPLD) Um Peripheriebausteine wie serielle Schnittstellen, Flash-Memory oder auch FPGAs in ein eingebettetes System zu integrieren, muß man die Registerstrukturen dieser Chips in den Adreßraum des Mikroprozessors einbinden. Dazu schließt man die Daten- und Adreßleitungen der Chips an den Systembus an. Da mehrere Peripheriegeräte „parallel“ am Bus angeschlossen sind, muß vor jedem Schreib- oder Lesezugriff ein Chip-Select-Signal generiert werden, welches einen Baustein auswählt. Steuersignale wie Chip-Select werden erzeugt, indem eine kombinatorische Logik die Bussignale des Mikrocontroller auswertet.

JTAG Port

3

JTAG Controller

In-System Programming Controller

I/O I/O

36 18

Funktion Block 1 Macrocells 1 to 18

36 18

Funktion Block 1 Macrocells 1 to 18

36 18

Funktion Block 1 Macrocells 1 to 18

36 18

Funktion Block N Macrocells 1 to 18

I/O

I/O Blocks I/O I/O I/O I/O I/O/GCK I/O/GSR I/O/GTS

3 1

FastCONNECT Switch Matrix

I/O

2or4

Abbildung 3.1 Xilinx XC9500 CPLD-Architektur ([Xil98/1])

Für die Implementierung solcher und anderer Funktionen werden in der Regel sogenannte komplexe programmierbare Logikbausteine (engl. Complex Programmable Logic Devices) eingesetzt. Das Einsatzgebiet der CPLDs liegt also hauptsächlich dort, wo gegenüber den FPGAs (siehe folgende Abschnitte) relativ wenige Logikgatter und viele I/O Blöcke benötigt werden. Die Verzögerungen sind gering. Typische Signallaufzeiten von Pin zu Pin liegen im

25

Bereich von nur 5 Nanosekunden. Die Verzögerungszeit läßt sich bei CPLDs vorhersagen. Dies ist möglich, weil die Verzögerungswerte der einzelenen Komponenten fest ist. Die CPLD-Architektur besteht im wesentlichen aus bis zu 16 Funktionsblöcken, einer FastConnect-Switch-Matrix sowie bis zu 192 I/O Blocks. Eine Übersicht über die Architektur findet sich in Abbildung 3.1. Im folgenden möchte ich diese Komponenten kurz erläutern: • Funktionsblöcke (engl. Function Blocks - FB): Ein Funktionsblock besteht aus 18 Makrozellen, welche die kombinatorische oder speichernde Logikfunktion erfüllen. In jeden FB führen 36 Signale hinein, welche aus der FastCONNECT Switch Matrix kommen. Jedes dieser Signale steht auch negiert zur Verfügung, so daß 72 Eingangssignale bereitstehen. Der Funktionsblock generiert 18 Ausgangssignale, die wieder zur FastCONNECT Matrix weitergeführt werden. Darüber hinaus ist jeder FB mit dem globalen Set/Reset Signal (GTS), den globalen System Taktleitungen (3 x GCK) und der System Reset Leitung (GSR) verbunden. Im FB geschieht nun folgendes: Die 72 Eingangssignale bilden den Input in ein programmierbares AND-Array. Dieses Array kann 90 Produktterme bilden. Jeder dieser Terme kann jeder Makrozelle zugeführt werden, wobei die Anzahl der Terme pro Makrozelle auf fünf beschränkt ist. Die Zuführung erfolgt dabei über einen sogenannten Produktterm-Allokator. In der Makrozelle werden die Produktterme in einer OR oder XOR Stufe zu einer kombinatorischen Funktion zusammengefaßt. In jeder Makrozelle ist ein Speicherelement vorhanden. Dieses kann als D- bzw. T-FlipFlop benutzt werden. • Schnelle Verbindungsmatrix (engl. FastCONNECT Switch Matrix): Dieser Matrix stehen alle I/O Blocks und alle Ausgangssignale der Funktionsblöcke zur Verfügung. Daraus können bis zu 36 Signale ausgewählt werden, die dann als Eingangssignale für die Funktionsblöcke dienen. • Eingangs und Ausgangsblöcke (engl. Input/Output Blocks - I/OB): I/O Blocks bilden die Schnittstelle zu den externen Pins des CPLD. Jeder dieser Blocks verfügt über einen Eingangs- und einen Ausgangstreiber. Es besteht die Möglichkeit, diese Treiber in einen hochohmigen Zustand zu schalten, welcher auch als Tri-State bezeichnet wird. Diese Maßnahme ist notwendig, um undefinierte Logikpegel zu vermeiden. Die Speicherung der Informationsbits in den Funktionsblöcken, der Switch Matrix und den I-O Blöcken beruht auf der FLASH Technologie. Flashspeicher behalten auch nach dem Abschalten der Betriebsspannung ihre Information.

26

Grundlagen

3.2.2 SRAM basierte FPGA FPGA (Field Programmable Gate Arrays) ermöglichen im Gegensatz zu CPLDs die Implentierung wesentlich komplexerer Logikdesigns. Damit eignen sie sich zur Emulation von anwendungsspezifischen Hardwarekomponenten. Im folgenden möchte ich SRAM basierte FPGA Architekturen anhand der XC4000 und Virtex Serie der Firma Xilinx im Überblick erläutern. Die Beschreibung einer Anwendung der XC4000 Serie folgt in einem späteren Kapitel.

3.2.2.1 Die XC-4000 Serie Im wesentlichen besteht ein XC4000 FPGA aus folgenden Komponenten: • Konfigurierbaren Logikblöcken (Configurable Logic Blocks -CLB) • Ein- und Ausgabeblöcken (IOBs) • Konfigurierbaren Verbindungen der CLBs und IOBs • Takttreibern, Tri-State Treibern und anderen speziellen Funktionseinheiten • SRAM-Zellen, die der Speicherung der Konfiguration dienen Wesentlich ist, daß die Speicherung der Konfiguration in SRAM-Zellen geschieht. Im Gegensatz zum FLASH basierten CPLD geht diese Information nach dem Abschalten der Betriebsspannung verloren. Die Initialisierung eines FPGA muß also nach jeder Inbetriebnahme des Systems erneut erfolgen. Dies kann z.B. durch den Mikroprozessor erfolgen. Eine weitere Möglichkeit besteht darin, die Konfigurationsdaten in einem seriellen EEPROM zu halten. Der FPGA ist in der Lage, nach dem Einschalten der Betriebspannung diese Informationen auszulesen und sich selbst zu konfigurieren (serieller Master Mode). Eine im FPGA implementierte Zustandsmaschine ermöglicht dies. Sowohl die Konfigurierung durch den Mikroprozessor als auch mit Hilfe des EEPROM waren Gegenstand dieser Arbeit und werden in folgenden Kapiteln näher erläutert. Im weiteren Verlauf dieses Abschnittes möchte ich die Komponenten der XC4000 Serie vorstellen: CLBs: Abbildung 3.2 zeigt den Aufbau eines konfigurierbaren Logikblocks. Diese Blöcke implementieren die eigentlichen Logikfunktionen. Zwei Funktionsgeneratoren (F und G) verfügen über jeweils 4 Eingangssignale. Aus diesen können sie jede beliebige logische Funktion berechnen (F’ und G’). Die Funktion wird dazu in Form einer Look-Up-Table (LUT) beschrieben, z.B:

27

F1 0 0 0 0 ... 1

F2 0 0 0 0 ... 1

F3 0 0 1 1 ... 1

F’ F’1 F’2 F’3 F’4 ... F’16

F4 0 1 0 1 ... 1

Tabelle 3.2: Look Up Table Die Eingänge adressieren 16 SRAM Zellen, in denen Funktionswerte als 0 bzw. 1 abgelegt sind. Desweiteren findet sich im CLB ein dritter Funktionsgenerator (H) mit 3 Eingängen. An die Inputs von H werden die Ausgangsignale der Generatoren F und G geführt, an den dritten Eingang Signale, welche ihren Ursprung außerhalb des CLB haben. Aus diesen 3 Inputs kann ebenfalls jede logische Funktion (H’) generiert werden. 4 C1..C4

H1

DIN/H2

SR/H0

EC

LOGIC G3

Din F’ G’ H’

FUNCTION OF

G’

Bypass

S/R CONTROL

G4

D

SD

YQ Q

G1-G4

G2 G1

LOGIC FUNCTION OF F’, G’ H’ AND H1

F4

EC RD

G’ H’

1 Y S/R CONTROL

LOGIC F3

FUNCTION F’ OF

F2

F1-F4

Din F’ G’ H’

Bypass

D

SD Q

XQ

F1 EC K (CLOCK)

H’

1 X

F’ Multiplexer Controlled by Configuration Program

Abbildung 3.2 Xilinx XC4000 konfigurierbarer Logikblock (Quelle: [Xil98/1])

28

Grundlagen

Zwei flankengesteuerte D-FlipFlops erlauben die Speicherung der Funktionsergebnisse F’, G’ und H’ sowie eines externen Signals. Die gespeicherten Werte werden über die Ausgänge XQ und YQ dem Verbindungsnetzwerk zur Verfügung gestellt. Der CLB besitzt zwei weitere Ausgänge. F’ oder G’ können mit dem ersten Output X verbunden werden, H’ mit dem zweiten Ausgang Y. Ein CLB kann für die Implementierung jeder der folgenden Funktionen1 benutzt werden: • Jede Funktion mit bis zu 4 Variablen, eine zweite Funktion mit bis zu vier unabhängigen Variablen plus einer dritten Funktion mit maximal 3 unabhängigen Variablen. Sollen drei Funktionen gleichzeitig generiert werden, muß ein Ergebnis mittels eines FlipFlops gespeichert werden • Jede Funktion mit maximal fünf Variablen • .Jede Funktion mit fünf Variablen plus einigen Funktionen mit sechs Variablen • Einige Funktionen mit bis zu neun Variablen

C1..C4

4

WE

D1

D0

EC

Write Decoder 1 of 16 Latch Enable G1..G4

F1..F4 K (CLOCK)

Write Decoder 1 of 16 Latch Enable

M U X

G’

Write Pulse Read Access

4

4

16-Latch ARRAY

16-Latch ARRAY

M U X

Write Pulse Read Access

Abbildung 3.3 Xilinx XC4000 Dual-Port-SRAM Block (Quelle:[Xil98/1])

1. Im Rahmen dieser Abhandlung werden nur logische Funktionen betrachtet

F’

29

Ein CLB muß nicht zwingend für die Berechnung logischer Funktionen verwendet werden. Es ist möglich, die SRAM-Zellen, welche eigentlich zur Speicherung der Look-Up-Tables dienen, als feinkörniges On-Chip-SRAM zu nutzen. Diese Eigenschaft wird in der Literatur auch oft als fine-grained SRAM capability bezeichnet. Die SRAM Blöcke können level sensitive, edge triggered oder dual port edge triggered konfiguriert werden. Abhängig vom Betriebsmode kann ein einzelner CLB als 16x2, 32x1 oder auch als 16x1 Bit-Array betrieben werden, wie Tabelle 3.3 zeigt.

16x1

16x2

32x1

Edge Triggered Timing

Level Sensitive Timing

Single-Port

ä

ä

ä

ä

ä

Dual-Port

ä

ä

Tabelle 3.3: Unterstützte RAM Modes (Quelle:[Xil98/1])

Ein Funktionsgenerator kann also zur Speicherung von 16 Bit benutzt werden (16x1). Durch Kombination von zwei Generatoren ist die Speicherung von 32 Bits möglich (32x1). Alternativ können auch 16 Worte mit einer Breite von zwei Bit abgelegt werden (16x2) Im Level-Sensitive-Mode dient ein externes Signal (Write Enable) als write-strobe, während im flankengesteuerten Betrieb dieses Signal mit dem Taktsignal verknüpft wird. Das Timing im flankengesteuerten Betrieb ist also im Gegensatz zum Level-Sensitive-Mode synchron. Abbildung 3.3 beschreibt einen CLB im 16x1 Dual Port Mode mit einem flankengesteuerten Timing. Für Funktionsgenerator F ist die Lese- und Schreibadresse immer identisch, da F1 bis F4 sowohl mit dem Write-Decoder als auch mit dem Read Decoder (MUX) verbunden sind. F verhält sich also wie ein 16x1 Single-Port-Array. Bei Funktionsgenerator G hingegen sind Lese- und Schreibadresse entkoppelt. Die Leseadresse bildet sich aus den CLB-Eingängen G1 bis G4, während die Schreibadresse durch F1 bis F4 bestimmt wird. Den Ausgang des Generators G bezeichnet man Dual-Port-Out, kurz DPO. Benutzt man [G1..G4] als Leseport und [F1..F4] als Schreibport, so läßt sich ein FIFO realisieren, auf das gleichzeitige Lese- und Schreibzugriffe möglich sind. Datenausgang ist dann DPO. Ein / Ausgangsblöcke (IOB): Um die interne Logik mit den externen Gehäusepins des FPGA zu verbinden, dienen sogenannte Ein/Ausgangsblöcke. In der Literatur ist ebenfalls die englische Bezeichnung In/Output Blocks (IOB) üblich. Ein IOB kann sowohl als Eingang, als Ausgang oder auch bidirektional konfiguriert werden.

30

Grundlagen

Slew Rate Control

Passive Pull-Up/ Pull-Down

T

Out

Flip-Flop D Q Output Buffer

CE

Pad

Output Clock I1

I2

Flip-Flop/ Latch Q

Clock Enable

Input Buffer

D

Delay

CE

Input Clock

Abbildung 3.4 Xilinx XC4000 Ein- Ausgabeblock (Quelle: [Xil98/1])

Die Eingänge der XC4000E Serie sind global für TTL oder CMOS Pegel konfigurierbar, ebenso die Ausgänge. Die Einstellungen für Ein- und Ausgangsblöcke sind unabhängig voneinander. XC4000XL FPGAs sind ebenfalls TTL- und CMOS-kompatibel, allerdings mit einem CMOS Pegel von 3.3 V Eingangssignale können mittel eines flankengetriggerten FlipFlops oder eines Level-Sensitiven Latches gespeichert werden. Das Ausgangssignal kann invertiert oder direkt zum Pad weitergereicht werden. Eine Zwischenspeicherung ist ebenso möglich (flankengesteuertes D-FlipFlop). Die Ausgangspuffer lassen sich in einen hochohmigen Zustand schalten. Die Implementierung von Tri-State-Outputs bzw. oder Tri-State-Ausgängen für bidirektionale Signale wird damit möglich. Konfigurierbare Verbindungen Alle internen Verbindungen bestehen aus metallischen Leitungssegmenten. Um eine konfigurierbare Verbindung zwischen den Komponenten des FPGA zu ermöglichen, existieren programmierbare Verbindungspunkte und Verbindungsmatrizen. Für die Beschreibung dieser Verbindungen ist auch der Begriff Routing Resources üblich.

31

12 Quad 8 Single 4 Double 3 Long CLB

2 Direct Connet 3 Long

12

4

4

Quad

Long

Global Nets

8

4

8

4

2

Long Double Single Global Nets

Direct Conncets

Carry Chain Abbildung 3.5 High Level Routing Diagramm für XC4000 Serie (Quelle: [Xil98/1])

Wie funktioniert die Programmierung dieses Netzes? Das Prinzip besteht darin, die Verbindung durch einen Pass-Transistor herstellen oder trennen zu lassen. Dieser Transistor wird vom Ausgang einer Speicherzelle (SRAM) getrieben. Somit ist es möglich, auch das Routing Design in SRAM-Zellen zu speichern. Wie Abbildung 3.5 zeigt, existieren ein einem XC4000 FPGA verschiedene Gruppen von Verbindungsleitungen: Single-, Double- und Quad-Length-Lines sowie sogenannte DirectConnects. Desweiteren finden sogenannte Long-Lines und Global-Nets Verwendung. Single-Length-Lines verbinden Switchmatrizen paarweise untereinander. Pro Verdrahtungskanal laufen immer acht dieser Leitungen sowohl in horizontaler als auch in vertikaler Richtung. Double-Length-Lines verbinden ebenfalls Switchmatrizen, im Gegensatz zu den SingleLength-Lines aber immer nur jede zweite Matrix. Es gibt vier Leitungen für die horizontale und noch einmal vier Stück für die vertikale Richtung. Quad-Length-Lines überspringen immer zwei Switchmatrizen. Es existieren pro Kanal je 12 Leitungen für beide Richtungen. Direct Connects verbinden keine Switchmatrizen, sondern konfigurierbare Logikblöcke untereinander. Die Verbindung erfolgt in vertikaler Richtung. Es stehen pro Kanal zwei Leitungen zur Verfügung. Da Direct Connects die Swichtmatrizen umgehen, bieten sie eine sehr schnelle Verbindung zwischen zwei CLBs. Long-Lines erstrecken sich über den ganzen Verdrahtungskanal. Es gibt von ihnen pro Kanal drei Stück, die horizontal verlaufen. Hinzu kommen acht für die vertikale Richtung.

32

Grundlagen

Um spezielle Signale (z.B Clock, Reset usw.) zu verteilen, existieren die Global Nets. Es gibt acht getrennte Global Nets in der XC4000-Architektur. Die Leitungen verlaufen über die gesamte Fläche des FPGA . CLB

CLB

CLB

Six Pass Transistors Per Switch Matrix Interconnect Point Doubles

PSM

Singles

PSM

Doubles CLB

CLB

PSM

CLB

{

PSM

CLB

CLB

Double

{

Singles Double

Singles

CLB

Double

Abbildung 3.6 Switchmatrizen (Quelle: [Xil98/1])

Bei der Beschreibung des Verbindungsleitungen wurden bereits mehrmals die Switchmatrizen erwähnt. Diese Matrizen dienen dazu, Leitungssegmente entweder miteinander zu verbinden (bei gleichbleibender Richtung) oder eine Richtungsänderung zu bewirken. Also “Weiche“ wird dient ein bereits zu Beginn des Abschnitts erwähnter Pass-Transistor, welcher durch eine SRAM-Zelle gesteuert wird. Abbildung 3.6 zeigt, wie die Switchmatrizen in die FPGA Architektur integriert sind. Die mit „CLB“ gekennzeichneten Elemente sind konfigurierbare Logikblöcke, „PSM“ die (programmierbaren) Switchmatrizen. Zusammenfassung In diesem Abschnitt wurde die Architektur der Xilinx XC4000 Serie vorgestellt. Folgende wesentliche Eigenschaften sollte der Leser im Gedächtnis behalten, da sie vor allem für das Verständnis der Abschnitte von Bedeutung sind, die sich mit der Problematik der Run Time Rekonfiguration auseinandersetzen: • Ein XC4000 FPGA ist beliebig oft rekonfigurierbar. • Bei einer Konfigurierung wird der gesamte FPGA neu geladen, eine bereichsweise Konfigurierung ist nicht möglich. • Die Konfiguration dauert lange (mehrere Millisekunden, abhängig vom Typ).

33

3.2.2.2 Virtex Die Virtex Serie ist die z.Z. leistungsfähigste FPGA Architektur der Firma Xilinx. Taktfrequenzen bis zu 200 Mhz und die Möglichkeit, bis zu 1 Million Gates zu implementieren (Quelle: [Xil99]), eröffnen dieser Architektur Anwendungen im High-End-Bereich. Wie die XC4000 Architektur verfügt jeder Virtex-FPGA über konfigurierbare Logikblöcke sowie über Ein-Ausgabeblöcke (IOBs).

DLL

IOBs

DLL

CLBs

VersaRing BRRAMs

IOBs

VersaRing BRRAMs

VersaRing

IOBs

VersaRing DLL

IOBs

DLL

Abbildung 3.7 Virtex Architektur (Quelle: [Xil99])

Die CLBs sind durch eine General Routing Matrix (GRM) miteinander verbunden. Diese Matrizen beinhalten ein Array von Routing Switches, welche sich an den Verbindungspunkten der vertikalen und horizontalen Verbindungskanäle befinden. Die Verbindung zwischen den CLBs und den IOBs erfolgt durch einen sogenannten Versa Ring, welcher die CLBs umschließt. Abbildung 3.7 zeigt die Virtex-Architektur im Überblick. Neben den bisher erwähnten Komponenten sind dort sogenannte Block RAMs (BRAMs) zu erkennen. BRAMs sind eingebettete SRAM-Blöcke. Neben den bereits in der XC4000-Architektur vorhandenen SRAM-Fähigkeiten in den Look-Up-Tables (LUT) stellen diese eine weitere Ressource für schnelle Speicherkapazitäten dar. Weiterhin bietet die Virtex-Architektur für jeden globalen Clock-Input-Buffer ein sogenanntes Delay Locked Loop (DLL). Diese Einrichtung dient dazu, Signalverschiebungen zu beseitigen, welche zwischen dem Clock-Input-Pad des Chips und den internen Clock-Input-Pins durch Laufzeitunterschiede entstehen. Die DLLs sorgen also dafür, daß im gesamten Chip ein synchrones Taktsignal angeboten wird. Die Konfigurierbaren Logikblöcke bestehen bei Virtex-FPGAs aus zwei identischen Teilen (Slices). In jedem Slice finden sich zwei Logic Cells (LCs). Eine solche Zelle besteht aus einem Funktionsgenerator, Carry-Logik und einem Speicherelement (FlipFlop). Die CarryLogik ermöglicht die effiziente Implementierung sehr schneller Arithmetikfunktionen. So kann z.B. ein Volladdierer mit nur einer Logic Cell realisiert werden.

34

Grundlagen

Cout G4 G3 G2 G1

LUT

Carry & Control

BY

F4 F3 F2 F1

Cout

SP D Q EC RP

LUT

Carry & Control

BX

SP D Q EC RP Slice 1

YB Y G4 G3 YQ G2 G1

YB Y LUT

Carry & Control

BY XB X F 4 F3 XQ F 2 F1

SP D Q EC

YQ

RP XB X LUT

Carry & Control

BX

Cin

SP D Q EC

XQ

RP Slice 0 Cin

Abbildung 3.8 2-Slice Virtex CLB (Quelle: [Xil99])

Die IOBs der Virtex Architektur sind wesentlich flexibler konfigurierbar als die der XC4000 FPGAs. Die I/O Blöcke sind in acht Gruppen zusammengefaßt, wobei jeder Gruppe eine andere Versorgungsspannung zugeordnet werden kann. Virtex FPGA sind daher zusammen mit unterschiedlichsten elektrische Schnittstellen einsetzbar, z.B. PCI und ISA Bussen. Virtex FPGAs sind wesentlich schneller und flexibler konfigurierbar als XC-4000 Bausteine. So existiert im Gegensatz zur XC4000-Architektur ein paralleler Lademodus. Desweiteren ist es nicht zwingend notwendig, den gesamten FPGA neu zu konfigurieren. Die Virtex-Architektur erlaubt es, ausgewählte Bereiche zur Laufzeit neu zu laden. Zusammenfassung: Ich möchte folgende Eigenschaften der Virtex-FPGA festhalten: • Virtex FPGA verfügen über Eigenschaften wie hohe Taktfrequenz, flexible I/O Anbindung und die Möglichkeit, sehr umfangreiche Logikschaltungen zu implementieren. • Die Virtex Architektur erlaubt eine schnelle und partielle Rekonfigurierbarkeit. Die FPGAs der Virtex Serie sind damit zumindest potentiell geeignet, spezielle Hardware zu entwerfen, welche für spezielle Aufgaben durch Anwendung von Run Time Rekonfiguration eine Performance erzielen kann, die über der von aktuellen High End Prozessoren (z.B. Power PC G3) liegt. Leider befindet sich die Hardware für eine Virtex basierte Emulationsplattform am Forschungszentrum Informatik Karlsruhe zur Zeit noch in Entwicklung, so daß sie im Rahmen dieser Arbeit nicht weiter untersucht werden konnte.

35

3.2.3 Antifuse basierte FPGA (Actel) Actel FPGAs sind im Gegensatz zu den SRAM basierten Bausteinen von Xilinx nicht rekonfigurierbar. Ist ein Actel FPGA programmiert, so ist diese Konfiguration dauerhaft. Dies hat auch zur Folge, daß die Konfiguration im Chip auch nach dem Abschalten der Betriebsspannung erhalten bleibt. Ich möchte im folgenden die Antifuse-Technologie am Beispiel der Actel Act-1 Serie kurz vorstellen. Antifuse basierte FPGAs bestehen im wesentlichen aus einer Anzahl von Logikmodulen, I/O Buffern sowie horizontalen und vertikalen Verbindungsleitungen (Abbildung 3.9). An den Kreuzungspunkten der Leitungen befinden sich sogenannte PLICE Antifuses, welche die Leitungen zunächst voneinander isolieren. Während der Programmierung werden diese Antifuses adressiert und programmiert. Dadurch werden die Logikmodule so verschalten, wie es die Schaltung erfordert, welche implementiert werden soll.

Abbildung 3.9 ACT 1 Device (Quelle: [Act94])

Ein Logikmodul der ACT-1 Serie ist relativ einfach aufgebaut. Es besteht aus nur wenigen logischen Gattern. Jedes Logikmodul kann folgende logische Funktion generieren ([KW99]): Y = S 1 ∧ B 0 ∧ D 00 ∨ S 1 ∧ B 0 ∧ D01 ∨ S1 ∧ B 1 ∧ D 10 ∨ S 1 ∧ B 1 ∧ D 11 mit S 1 = A 0 ∨ A 1

36

Grundlagen

Abbildung 3.10 ACT-1 Logikblock

Die Daten und Steuerleitungen der Multiplexer werden mit den Signalen der Verdrahtungskanäle verbunden. Dabei kann auch auf Signale zugegriffen werden, die permanent Low (logisch 0) oder High (logisch 1) sind. Durch entsprechende Verdrahtung kann ein solcher Logikblock vier logische Grundfunktionen (NAND, AND, OR, NOR) für zwei, drei oder vier Variablen berechnen. Ebenso ist die Implementierung von D-Latches und Funktionen wie XOR , AND-OR und OR-AND Funktionen möglich.

37

3.3 Speicherbausteine 3.3.1 EEPROMs Wie EPROMs1speichern auch Speicherbausteine der EEPROM2-Technologie Informationen dauerhaft, d.h. auch nach dem Abschalten der Betriebsspannung.

Abbildung 3.11 EEPROM Speichertransistor (Quelle: [HM93])

Die dauerhafte Speicherung der Daten wird mit einem Speichertransistor realisiert. Dieser Transitor ist ein MOSFET3, welcher zusätzlich über ein sogenanntes Floating Gate verfügt. Dieses Floating Gate befindet sich zwischen dem Substrat und dem Steuer Gate und ist vollständig isoliert. Das Gate hingegen ist üblicherweise mit einer Wortleitung verbunden, das Drain mit einer Bitleitung und die Source mit einem Referenzpotential (z.B. Vcc). Der Speichertransistor wirkt als Schalter zwischen Wort- und Bitleitung. Ein normaler MOS-Transistor würde bei aktivierter Wortleitung über Drain, Kanal und Source Vcc verbinden und somit auf High-Pegel treiben, der Transitor speichert eine Eins. Ist das Floating Gate neutral, verhält sich der Speichertransistor ebenso. Anders ist es, wenn das Floating Gate negativ geladen ist. Die Ladung des Floating Gate schirmt dann das Feld des Steuer Gates ab. Somit kann der Transitor auch bei aktivierter Wortleitung nicht durchschalten, da das Steuer Gate kein ausreichend starkes Feld aufbauen kann. Der Speichertransistor speichert eine Null. Die Beladung des Floating Gates geschieht durch einen elektrischen Impuls mit einer Spannung von 20 V und ca. 50 ms Dauer. Durch diesen Impuls entstehen im Kanalbereich schnelle Ladungsträger (Elektronen), die genug Energie besitzen, die Isolierschicht zwischen 1. EPROM = Electrical Programmable ROM 2. EEPROM = Electrical Erasable PROM 3. MOSFET = Metal-Oxid-Semiconductor Field Effect Transistor

38

Grundlagen

Substrat und Floating Gate zu überwinden. Danach sind sie im sind gut isolierten Floating Gate „gefangen“, da sie nach Abkühlung zu wenig Energie besitzen, um die Isolierschicht nochmals zu durchdringen. Die Ladungen bleiben über sehr lange Zeiträume (mindestens zehn Jahre) erhalten. Die Programmierung eines EEPROM erfolgt also nach dem gleichen Prinzip wie die eines EPROM. Während ein EPROM nur mit UV-Licht gelöscht werden kann (durch das Licht erhalten die Elektronen im Floating Gate Energie, um es zu verlassen), ist das Löschen eines EEPROM durch einen elektrischen Spannungsimpuls möglich. Dazu verfügt der Speichertransistor gegenüber einem EPROM über eine Schicht mit dünnerem Tunneloxid. Diese befindet sich zwischen dem Substrat und einem Teil des Floating Gates, welcher nach unten gezogen ist. Zum Entladen des Floating Gates legt man einfach eine umgekehrte Spannung zwischen Gate und Drain an. Dadurch bekommt das Drain gegenüber dem Gate ein positives Potential und die Elektronen genug Energie, um die Isolierschicht zu überwinden und damit das Floating Gate zu verlassen. Dabei muß man aber darauf achten, daß dieser Prozeß nicht zu lange dauert. Ansonsten verlassen zu viele Elektronen das Floating Gate und es erhält ein positives Potential. Das Resultat wäre eine verschobene Transistorkennlinie, die den weiteren normalen Betrieb verhindern würde. Glücklicherweise generieren Schaltkreise wie die seriellen EEPROMs von Atmel ihre Schreib- und Löschzyklen intern, so daß sich der Programmierer nicht auch noch mit den Tücken der Physik plagen muß.

39

3.4 Rekonfigurationsmethoden für FPGA Rekonfigurierbare Logik ist eine Architektur, welche den Entwurf von flexiblen Systemen ermöglicht, welche durch ihr anwendungsspezifisches Design in Bereichen eingesetzt werden können, die selbst den schnellsten Mikroprozessoren und Supercomputern verschlossen bleiben. Ein weiterer Vorteil ist, daß einmal entworfene Designs in späteren Projekten wiederverwendet werden können, ähnlich wie Softwarebibliotheken.

3.4.1 Compile Time Rekonfiguration (CTR) Compile Time Rekonfiguration ist die einfachste Methode der Rekonfiguration von programmierbaren Logikschaltkreisen. Typisch für die CTR Methode ist, daß die Designdaten in das FPGA geladen werden und dann unverändert bleiben, während die Anwendung ausgeführt, d.h. das FPGA benutzt wird. Es existiert also nur eine systemweite Konfiguration.

Konfigurieren

Ausführen

Abbildung 3.12 Compile Time Rekonfiguration([HuWi95])

Eine typische und weit verbreitete Anwendung für CTR ist die bereits im Abschnitt 2.1.5 beschriebene Emulation von ASICs. Dabei ist die Strategie der statischen Hardware Allokation die Eigenschaft, die das klassische ASIC Design mit der CTR Methode verbindet. Die CTR Methode stellt keine speziellen Anforderungen an die FPGA Architektur, da es prinzipiell ausreichend ist, das FPGA einmalig konfigurieren zu können.

3.4.2 Run Time Reconfiguration (RTR) Im Gegensatz zur CTR verfolgt die Methode der Run Time Rekonfiguration einen anderen Ansatz. Statt eine Konfiguration in das FPGA zu laden, welche die gesamte logische Funktionalität enthält, entwirft man mehrere Konfigurationen. Diese Konfigurationen werden während der Laufzeit der Anwendung nachgeladen.

Konfigurieren

Ausführen

Abbildung 3.13 Run Time Rekonfiguration ([HuWi95])

40

Grundlagen

RTR Applikationen verfolgen also eine Strategie der dynamischen Hardware Allokierung. Hadley und Hutchings unterteilen in [WirHut97] die Run Time Rekonfiguration dabei in globale und lokale Run Time Rekonfiguration. • Globale Run Time Rekonfiguration lädt bei jeder Rekonfiguration das gesamte FPGA neu. Diese Vorgehensweise ist natürlich nur dann sinnvoll, wenn sich die Anwendung in sogenannte Time-Exclusive-Segments aufteilen läßt. Zeitexklusive Segmente dürfen nicht gleichzeitig ausgeführt werden. Der Prozeß, der einen Algorithmus in diese zeitexlusiven Segmente zerlegt, wird als temporale Partitionierung bezeichnet. Ähnlich der strukturellen Partitionierung, bei der die Elemente des Schaltkreises in funktionell zusammengehörenden Gruppen angeordnet werden, hat der Prozeß der temporalen Partitionierung das Ziel, Elemente zusammenzufassen, welche gleichzeitig operieren. Ein Algorithmus wird temporal partitioniert, indem er in Phasen zerlegt wird. Jede dieser Phasen wird dann in einem eigenen FPGA Design implementiert. Weiterhin muß beim Design beachtet werden, daß die entstandenen Designkomponenten meist nicht unabhängig voneinander arbeiten können. Typischerweise verarbeitet jede Komponente Daten irgendwelcher Art, die an den Nachfolger weitergegeben werden, also auf irgendeine Weise gespeichert werden müssen. Eine Speicherung im FPGA selbst verbietet sich, da dieser ja bei der nächsten Rekonfiguration vollständig überschrieben wird. Somit muß der Systemdesigner andere geeignete Wege finden, um diese Inter-Configuration Kommunikation zu gewährleisten. Die Designs können dann in den FPGA geladen werden, wenn sie benötigt werden.

L1

R1

L2

R2

Ln

Rn

Li .. Konfiguration i laden (Load) Ri .. Konfiguration i benutzen (Run)

Abbildung 3.14 Globale Run-Time Rekonfiguration ([HuWi95])

Für globale Run Time Konfiguration eignen sich prinzipiell alle FPGA, welche sich beliebig oft neu programmieren lassen, also z.B die Bausteine der XC4000 Serie. Selbstverständlich ist die Zeit, welche zum Laden benötigt wird, ein limitierender Faktor. Daher sollte die Partitionierung so erfolgen, daß die Designs eine möglichst lange Zeit im FPGA verbleiben können, bevor ein Umladen erfolgen muß.

• Lokale Run Time Rekonfiguration geht einen Schritt weiter. Es wird nicht mehr das geamte FPGA neu konfiguriert, sondern nur Teile der Logik nachgeladen, während der Rest des Designs unverändert bleibt.

41

Anstatt relativ große, globale Partitionen zu laden wird versucht, nur die benötige Funktionalität zu jedem beliebigen Zeitpunkt nachzuladen. Dadurch kann der Zeitaufwand für das Nachladen verringert werden, da sich kleinere Konfigurationen natürlich schneller in das FPGA übertragen lassen. Ebenso ist es möglich, die Allokation von Hardware effizienter zu gestalten. Die Ressourcen, welche ein FPGA bietet, lassen sich mittels lokaler RTR besser ausnutzen. So erzielten Hadley und Hutchings im RRANN21 Projekt eine Reduzierung der Konfigurationszeit um 25 % und eine 50 prozentigen Erhöhung der Neuronendichte. ([HuWi95])

A

A

B ausführen

B ausführen

C

Abbildung 3.15 Lokale Run-Time Rekonfiguration ([HuWi95])

Applikationen, die sich gut mittels lokaler Run-Time Konfiguration realisieren lassen, basieren mehr auf einer funktionalen Aufteilung ihrer Komponenten als auf einer zeitlichen Partitionierung. Eine Applikation wird für die Implentierung in eine möglichst feinkörnige Menge von Operationen zerlegt. Diese Operationen müssen nicht zwingend zeitexklusiv sein. Beim Systemdesign sollte aber dennoch darauf geachtet werden, daß Komponenten, welche längere Zeit nicht benutzt worden, aus dem FPGA entfernt werden, um Ressourcen freizugeben. Lokale Run Time Konfiguration funktioniert nur mit FPGA, welche sich partiell nachladen lassen. Darunter zählen die Vertreter der XC6000 und Virtex Serie der Firma Xilinx.

1. Run-Time Reconfiguration Artificial Neural Network

42

Grundlagen

43

Kapitel 4

Entwicklungsumgebung Im diesem Kapitels möchte ich die bereits vorhandenen Hard- und Softwarekomponenten vorstellen, auf denen diese Arbeit aufbaut. Zunächst werde ich die Hardwareplattform SPYDER-CORE-P1 vorstellen. Weiterhin werden im Verlauf des Kapitels das Echtzeitbetriebsystem VxWorks sowie alternative Entwicklungsumgebungen besprochen.

4.1 Die Emulationsumgebung SPYDER-CORE-P1 SPYDER-CORE-P1 ist bereits die zweite Generation eines Emulationssystem für eingebettete Systeme, welches am Forschungszentrum Informatik in Karlsruhe entwickelt wurde. Seine Architektur wurde so gewählt, daß sie eine schnelle und kostengünstige Entwicklung eingebetteter Systeme ermöglicht. Besonders hervorzuheben ist dabei die Möglichkeit des parallelen Hard- und Softwareentwurfs. Das System besteht im wesentlichen aus zwei Komponenten: einer Emulationsumgebung für applikationsspezifische Hardware sowie einem Mikroprozessorsystem. Ein SRAM-basiertes FPGA (XC 4010E) der Firma Xilinx emuliert digitale Schaltungen. Der I/O Bereich dieses Bausteins ist über ein ISA-Bus Interface mit dem Host PC verbunden. Somit kann das FPGA vom PC aus konfiguriert werden und die digitale Schaltung auf Korrektheit überprüft werden, indem über den ISA-BUS Testmuster an das FPGA geschickt werden. Weiterhin ist das FPGA mit einem Erweiterungsstecker verbunden, welcher den Anschluß von kundenspezifischer diskreter Hardware erlaubt. Darüber hinaus ist es auch in den Adreßraum des Mikrocontrollers eingebunden. Zur automatischen Konfiguration des FPGA ist ein serielles EEPROM der Firma Actel vorhanden. Die Programmierung dieses Chips kann ohne ein externes Programmiergerät erfol-

44

Entwicklungsumgebung

gen. Ermöglicht wird dies durch das Tool ELOAD. Eload ist Teil der Softwareumgebung für Spyder Core und entstand im Rahmen dieser Arbeit. Eine Beschreibung findet der Leser in Kapitel 5.

Logik-Analysator Stecker I

XC6000 oder Actel FPGAs

Basis-Platine 8 Bit I/O Bus

1

CAN 2 Hauptspeicher DRAM-Bänke 1-128MB

ErweiterungsStecker

embedded PowerPC PPC403GCX

2

ASICs

Umgebung

Logik-Analysator Stecker II

25..80 MHz Mikrocontroller-Bus dedizierte Hardware

LAN/ WAN

25..40MHz Bus FLASH 2..8MB Ethernet

Treiber und KontrollLogik CPLD

10Mbit 2 serielle Ports DPRAM 2kB

Schnittstellen FPGA XC4000 85.000Gates

AT-ISA Bus CPLD 3

1 MHz

Abbildung 4.1 Blockschaltbild von SPYDER-CORE-P1 (Quelle: [KW99])

Die Mikrocontroller-Partition von SPYDER CORE baut auf einer Power PC CPU der Firma IBM auf. Zum Einsatz kommen dabei die für den Embedded-Bereich angepaßten Controller Power PC 403GA und 403GCX. Diese Chips zeichnen sich durch eine einfache Integrierbarkeit in ein eingebettetes System aus, da sie u.a. über On-Chip-Funktionalitäten wie DRAM-Controller und serieller Schnittstelle verfügen. Durch ihre Taktfrequenzen von 40 bis 80 Mhz eignen sich die Prozessoren für den Einsatz von (Echtzeit) Betriebssystemen. Ein EPROM, 8 MByte Flash-Memory und Steckplätze für Standart-PS/2-Simm gestatten eine flexible Systemkonfiguration. Die Kommunikation mit dem Entwicklungs-PC unterstützt eine Ethernet-Schnittstelle. Der Mikrocontrollerkern von SPYDER-CORE-P1 dient vor allem dazu, Software zu testen, welche für eine bestimmte Plattform entwickelt wurde. Dies kann bereits geschehen, wenn diese Zielplattform noch gar nicht in Hardware zur Verfügung steht. Vielmehr kann Applikations-

45

spezifische Hardware durch das FPGA emuliert und zusammen mit der auf dem Mikrocontroller laufenden Anwendung getestet werden.

4.2 Entwicklungsumgebung SDS Die Softwareentwicklung für eingebettete Systeme findet in der Regel auf einem Host-PC statt. Auf diesem ist ein Cross Compiler installiert, welcher Maschinencode erzeugt, der auf der Zielplattform lauffähig ist.

Abbildung 4.2 SDS Monitor Software

SDS ist ein Cross-Debugger. Über ein EPROM wird auf dem Target ein kleines Programm gestartet, welches mit der auf dem Host-PC laufenden Entwicklungsumgebung kommuniziert. Die Kommunikation erfolgt dabei über die seriellen Schnittstellen, über die sowohl der PC als auch die Targetplattform (hier SPYDER-CORE-P1) verfügen. SDS ermöglicht dem Entwickler, am PC eine Anwendung zu erstellen, mittels eines Cross Compilers zu übersetzen und dann über die serielle Schnittstelle auf die Zielhardware zu laden und zu starten. Dort kann das zu testende Programm schrittweise abgearbeitet und auf Fehler analysiert werden. SDS ist kein Betriebssystem und bietet auch keinerlei Kommunikationsmöglichkeiten wie die Funktion printf der Programmiersprache C oder gar Netzdienste. Der Programmierer muß

46

Entwicklungsumgebung

die gesamte Verwaltung der Hardware selbst organisieren. SDS eignet sich daher nur zum Test kleiner Applikationen.

4.3 Echtzeitbetriebsystem RTEMS1 Ursprünglich wurde RTEMS vom Militär entwickelt und steht heute als Open-SourceSoftware zur Verfügung. RTMES ist ein vollständiges Betriebssystem, welches die Verwaltung von Tasks, Semaphoren, Interrupts usw. realisiert.

Abbildung 4.3 RTEMS Architektur (Quelle: [Army96])

Durch die Einbindung einer C-Library ist auch die Benutzerkommunikation mittels üblicher Funktionen wie printf möglich. RTEMS liegt als Bibliothek vor. Eine Anwendung oder auch eine andere Library wie libc benutzt RTEMS-Funktionen, welche durch einen Linker in die Applikation eingebunden werden. Jede gelinkte RTEMS-Applikation stellt also ein monolithisches File dar, welches alle Anwendungs- und Betriebssystemfunktionen beinhaltet. RTEMS wurde von mir im Rahmen meiner Studienarbeit auf das Vorgängermodell des Spyder Prozessorboards portiert und im Verlauf der Diplomarbeit an die Spyder Architektur angepaßt. Die Übersetzung von RTEMS basierter Software erfolgt wieder mittels eines Cross Compilers, zur Übertragung auf das Target dient ein spezieller Loader, welcher ebenfalls von mir implementiert wurde. Dieses Prinzip ist flexibel genug, so daß auch Anwendungen erstellt und geladen werden können, die keinen Gebrauch von RTEMS Funktionen machen. Das vorhandene Paket aus Cross Compiler, Loader und RTEMS bietet sich als Low-Cost Entwicklungsumgebung an, da es vollständig auf frei verfügbarer Software basiert.

1. Real Time Executive for Multiprozessor Systems

47

RTEMS diente im Rahmen dieser Arbeit vor allem zur Verifikation von Laufzeitmessungen für SPYDER-CORE-P1. Eine Beschreibung der Softwaretools für diese Laufzeitmessungen sowie des Loaders folgt in Kapitel 5.

4.4 Echtzeitbetriebsystem VxWorks VxWorks ist ein kommerzielles Softwareprodukt der Firma WindRiver. Gegenüber anderen Systemen wie RTEMS zeichnet es sich durch Features wie einen vollständigen TCP/IP Stack und vor allem durch eine sehr komfortable Entwicklungsumgebung aus. Zur Programmentwicklung dient wiederum ein auf dem Entwicklungs-PC laufender Cross Compilers. Der Programmcode wird in Objektfiles übersetzt und zunächst nicht gelinkt. Diese Objektfiles können auf das Targetboard übertragen werden, indem für die Kommunikation das TCP/IP Protokoll benutzt wird. Als Übertragungsmedium dient wie bei SPYDER-CORE-P1 z.B. Ethernet, aber auch andere physikalische Medien sind möglich.

Entwicklungsrechner (PC) Windows-NT

Zielsystem (SPYDER-CORE-P1) VxWorks

Kommunikations Treiber

Target Server

tWdb Task (Target Agent)

(tUser1)

(tUser2)

Netzwerk (Ethernet, Protokoll TCP/IP) Abbildung 4.4 Entwicklungsumgebung für VxWorks (Quelle: [KW99])

Um diese Kommunikation zu realisieren, findet ein sogenannter Target-Server Verwendung. Dieser kommuniziert mit einem auf dem Zielsystem laufenden Target-Agent. Der TargetAgent läuft als spezielle Task unter VxWorks. In Abbildung 4.4 ist dieses Prinzip schematisch dargestellt. Damit der Bootloader nach dem Einschalten zur Verfügung steht, muß er sich neben anderen wichtigen Initialisierungsroutinen auf einem nichtflüchtigen Speichermedium befinden, z.B einem EPROM oder Flash-Memory. Die Spyder-Architektur bietet beide Varianten für den initialen Start des Systems an. Target-Server und Target-Agent erlauben, das eigentliche Betriebsystem über eine Netzwerkschnittstelle zu booten. Desweiteren ist es möglich, die anfangs erwähnten Objektfiles zur Laufzeit in das Betriebssystem einzubinden. Ebenso ist der gezielte Aufruf von implementierten C-Funktionen durch eine Art Interpreter möglich. Die gesamte Funktionalität wie das Nachladen von Objektfiles, Messung des Ressourcenverbrauchs der laufenden Tasks usw. ist unter einer einheitlichen Bedienoberfläche zusammen-

48

Entwicklungsumgebung

gefaßt. Während das eigentliche Betriebsystem den Namen VxWorks trägt, wird das gesamte Paket aus Entwicklungsumgebung und RTOS unter der Bezeichnung Tornado vertrieben.

49

Kapitel 5

Implementierung In diesem Kapitel möchte ich die von mir entwickelte Softwareumgebung für das SPYDERCORE-P1 Emulationsboard beschreiben. Beginnen werde ich mit der Vorstellung eines Pakets von Diagnosetools, mit dessen Hilfe man auf der Emulationsumgebung testen kann, ob alle Hardwarekomponenten fehlerfrei arbeiten. Anschließend gehe ich auf ein Testverfahren ein, mit dessen Hilfe die Performance der eingesetzten Hard- und Softwarekomponenten meßbar gemacht wird. Es folgt die Beschreibung eines Tools für Entwickler, die das FPGA mittels Compile-Time Rekonfiguration nutzen. Weiterhin werde ich darlegen, wie sich SPYDER-CORE-P1 als Entwicklungs- und Forschungsplattform für Run Time Konfiguration verwenden läßt.

5.1 Diagnosesoftware Das vorliegende Paket von Diagnosetools ermöglicht einen umfassenden und systematischen Test der Funktionalität eines Spyder-Core Mikroprozessorsystems. Das Paket besteht aus neun Komponenten. Die Konzeption des Testsets lehnt sich dabei an der Initialisierungs-Sequenz von VxWorks an. So ist es möglich, zunächst rudimentäre Systemfunktionen wie den Adreß- und Datenbus zu prüfen, bevor weitere Baugruppen betrachtet werden. Jedes Testtool hat die Funktionsüberprüfung einer bestimmten Hardwarekomponenten zum Gegenstand. Bei der Spezifikation wurde vor allem darauf Wert gelegt, daß sich diese Komponenten möglichst unabhängig voneinander testen lassen, um eine Fehlerquelle eng eingrenzen zu können. Alle Tools liegen als EPROM Images vor, sind also ohne weitere Hardwarevorraussetzungen einsetztbar. Für einige Tools sind es jedoch erforderlich, externe Meßgeräte zu benutzen.

50

Implementierung

5.1.1 Grundtest 1 Durch den Einsatz dieses Programms kann man feststellen, ob der Prozessor und der Systembus prinzipiell arbeitsfähig sind. Dazu führt es folgendes minimales Assemblerprogramm aus:

################################################################## # SIMPLE 1 ( Carsten Nitsch, Feb. 1999) # # This .text region should be loaded at 0xFFFFFF00 in order to # map the branch instruction at 0xFFFFFFFC. ################################################################## .globlrvector .text .space0xFC rvector: ba rvector

Durch spezielle Metabefehle wird der Assembler veranlaßt, eine Branch Anweisung an der Adresse 0xFFFFFFFC abzulegen, welche einen Sprung auf genau diese Adresse ausführt. Die eben beschriebene Adresse ist die Einsprungstelle für Prozessoren der Power PC GA und GXC Serie, d.h. nach einem Power-On-Reset verarbeitet die CPU den an dieser Stelle liegenden Befehl zuerst. Der Mikrocontroller wird also auf dieser Adresse eine Endlosschleife abarbeiten. Im folgenden werde ich erläutern, auf welche Weise dieses Programm zum Funktionstest der Hardware eingesetzt werden kann. Dazu schließt man einen Logic Analyzer an den Adreßund Datenbus des Systems an. Beim Datenbus kann man sich auf die Auswertung der unteren acht Datenbits1 beschränken, da das EPROM ein 8-Bit-Device darstellt. Zusätzlich muß auch das Chip-Select Signal aufgenommen werden. Der Logic-Analyzer wird so konfiguriert, daß er die Daten- und Adreßsignale als Bus aufnimmt. Der Trigger wird auf die fallende Flanke des Chip-Enable Signals eingestellt, da dieses Signal low-aktiv ist. Wird nach einem Reset der CPU am Logic-Analyzer ein Trigger-Event ausgelöst, so zeichnet dieser die Aktivität auf den Bussen auf. Im fehlerfreien Fall muß die Aufzeichnung zeigen, daß Daten- und Adreßbus nacheinander wie folgt belegt waren:

1. Beim Power PC trägt das niederwertigste Bit die Nummer 31. Das niederwertigste Datenbit ist also D31 und nicht D0.

51

Takt 1 2 3 4

Adreßbus A31..A0 0xFFFFFFFC 0xFFFFFFFD 0xFFFFFFFE 0xFFFFFFFF

Datenbus D31..D24 0x4B 0xFF 0xFF 0xFE

Tabelle 5.1: Fetching Das Wort 0x4BFFFFFE ist die binäre Codierung des Sprungbefehls. Auswertung: • Am Logic Analyzer wird ein Trigger ausgelöst und die Busaktivitäten verhalten sich wie in Tabelle 5.1 dargestellt. In diesem Fall kann man davon ausgehen, daß die CPU funktioniert und wahrscheinlich keine groben Fehler (z.B. Kurzschlüsse) vorhanden sind. Eine vollständige Funktionsprüfung für die Busse (insbesondere Treiberbausteine) ist durch dieses Tool aber noch nicht möglich. • Am Logic Analyzer wird kein Trigger ausgelöst oder keine Busaktivitäten aufgezeichnet. In diesem Fall liegt ein ernster Fehler vor. In diesem Fall sollte an den LA1direkt am Steckerkranz2 anschließen, welcher die CPU umschließt. Sind jetzt Busaktivitäten meßbar, liegt die Fehlerursache möglicherweise in einem fehlerhaften Treiberbaustein. Treiberbausteine sind Chips, welche Peripheriegeräte wie EPROMs mit der CPU verbinden. • Die vom LA aufgezeichneten Bussignale sind falsch. Zur weiteren Fehleranalyse muß man die gemessenen Werte mit denen aus Tabelle 5.1 bitweise vergleichen. Dabei sollte man vor allem darauf achten, ob einzelne Signale permanent auf High oder Low liegen. Ein solches Verhalten gibt Hinweise auf die mögliche Fehlerursache, z.B. Kurzschlüsse zwischen den Signalen oder Leitungsunterbrechungen. Oft findet man den Fehler in Form von kalten oder unsauberen Lötstellen, die sich auch bei sorgfältiger Arbeit nie ganz ausschließen lassen. Auch ein defekter Treiberbaustein kann die Quelle solcher Bitfehler darstellen. Dieses Testprogramm eignet sich also vor allem zum Aufspüren von Fehlern auf den Bussen. Da diese Bitfehler eine häufige Ursache für „totale“ Ausfälle sind, ist es trotzt seine minimalen Umfangs ein nützliches Tool, mit dessen Hilfe schon einige Systeme wieder funktionstüchtig gemacht werden konnten.

1. Logic Analyzer 2. Der sogenannte Steckerkranz stellt alle Signale des Daten- und Adreßbusses für Messungen zur Verfügung

52

Implementierung

5.1.2 Grundtest 2 Dieses Tool überprüft alle Adreßleitungen auf ihre Funktionalität, welche für die Funktion des EPROMS notwendig sind. Der für das EPROM nutzbare Adreßraum ist 20 Bit breit, so daß die Leitungen A31..A12 relevant sind.1 Das Programm springt zu einer Adresse, welche am Beginn des EPROM Speicherraums steht. Bei einem 128 kByte EPROM liegt diese Stelle auf der Adresse 0xFFFFE000 im Adreßraum des Power PC. Das Tool initialisiert das Maschinen-Status-Register (MSR) und invalidiert die Befehls- und Datencaches. Weiterhin schaltet es alle Interrupts aus. Nach dieser Initialisierung des Mikroprozessors arbeitet das Testprogramm eine Endlosschleife ab. Dabei liest es fortlaufend den Adreßbereich des EPROM aus. Auswertung: Um das Tool zur Analyse der Spyder-Core Hardware benutzen zu können, benötigt man wiederum einen Logic Analyzer. Um einen Überblick über den Zustand der Hardware zu erhalten genügt aber auch ein Speicheroszilloskop. Wenn man das Timing auf dem Adreßbus aufzeichnet, so ist im fehlerfreien Falle folgendes Resultat zu erwarten:

A31

A30

A29

A28 Abbildung 5.1 Busaktivitäten

1. Beim Power PC bezeichnet das Bit mit dem Index 0 das höchstwertigste Bit.

53

Wichtig ist, daß auf allen Adreßleitungen (A31 bis A12) Pegelveränderungen sichtbar sein müssen. Zeigen einzelne Leitungen permanente Pegel, so liegt ein Fehler vor, wie er schon in Abschnitt 5.1.1 beschrieben wurde. z.B ein defekter Treiberbaustein. Anpassung des Testprogramms: • Der Beginn des Scanbereichs, im Programmcode mit _reset bezeichnet, muß am Beginn des Adreßbereichs des EPROM liegen. Dazu ist das Linkfile1 (corep1.lnk) entsprechend zu editieren. Das folgende Beispiel zeigt die Konfiguration für ein 128 kByte EPROM:

MEMORY { rom: org = 0xfffe0000, len = 0x1FF00 /* 128 kByte EPROM */ reset: org = 0xffffff00, len = 0x100 } SECTIONS { .init (TEXT) : { rvect403.o(.text) } > reset .vtbl (TEXT) : { init.o(.text) } > rom }

Für andere EPROMs lauten die Parameter z.B: rom:

org = 0xFFFC0000, len = 0x3FF00 /* 256 kByte */

rom:

org = 0xFFF80000, len = 0x7FF00 /* 512 kByte */

• IBM 403 GA / GCX CPUs unterscheiden sich u.a in der Größe ihrer Cache Lines. GA CPUs haben 32 Cache Lines Data Cache und 64 Lines Instruction Cache, während GCX CPUs über 256 Cache Lines Data Cache und 512 Lines Instruction Cache verfügen können. Die Definitionen für DATA_CACHE_LINES / INSTRUCTION_CACHE_LINES sind in init.s anzupassen.

1. Ein Linkfile enthält die Informationen darüber, in welche Adreßbereiche ein Programm bzw. Teile davon geladen werden sollen

54

Implementierung

5.1.3 Grundtest 3 Diese Programm dient darüber, sich einen schnellen Überblick über die Funktion des EPROMS und der 8 bit Bank zu verschaffen. Die sogenannte 8 Bit Bank ist ein für die Kommunikation mit peripheren Geräten konfigurierter Adreßbereich des Power PC. Die Kommunikation erfolgt elektrisch dabei über die bereits erwähnten Treiberbausteine. Über die 8-Bit Bank sind u.a. eine serielle Schnittstelle, das Ethernet Interface und 2 Leuchtdioden ansprechbar.

Abbildung 5.2 SPYDER-CORE-P1 mit LEDs

Das Tool initialisiert das MSR1, invalidiert die Daten- und Befehlscaches und schaltet alle Interrupts ab. Darüber hinaus konfiguriert es einen Teil des Power PC Adreßraums, daß dieser als 8-Bit I/O-Bank benutzt werden kann. Am Ende versucht das Programm, eine Leuchtdiode einzuschalten. Die betroffene LED ist in Abbildung 5.2 durch einen Pfeil gekennzeichnet. Zur Aktivierung der LED führt das Tool einen Schreibzugriff auf die Adresse 0xF2800000 aus und verbleibt dabei in einer Endlosschleife. Der folgende Programmausschnitt verdeutlicht das dieses Prinzip:

1. Maschinen Status Register

55

forever: lis r2, 0xF280 # Switch on green LED and wait for ever ori r2, r2, 0x0000 lis r1, FFFF ori r1, r1, FFFF stwx r1, 0, r2 ba forever

Abbildung 5.3 Grundtest 3

Ein spezieller programmierbarer Logikbaustein (Mach210A der Firma AMD) erkennt diese Zugriffe und generiert ein Signal, welches die Leuchtdiode einschaltet (LED Chip-Select). Auswertung: • Nach dem Einschalten oder nach einem Reset des Prozessors leuchtet die grüne LED auf. In diesem Falle kann man davon ausgehen, daß der Adreßbus und der Datenbus funktionsfähig sind sowie das EPROM und die Treiberbausteine korrekt arbeiten. Desweiteren ist kein Fehler beim Mach feststellbar. • Bleibt die LED dunkel, so können mehrere Fehlerquellen die Ursache dafür sein. Eine Möglichkeit ist, daß ein Fehler beim EPROM, Bus bzw. den Treiberbausteinen zu finden ist. Für eine genauere Untersuchung benutzt man dann die Tools Grundtest 1 und Grundtest 2. Funktionieren diese Komponenten, so sollte man den Mach-Baustein näher untersuchen. Zu diesem Zweck benutzt man am besten ein Oszilloskop. Dann überprüft man, ob am Pin, welcher die LED treibt, ein Low-Pegel zu messen ist (das LED Chip-Select ist low-aktiv). Ist dies der Fall, so ist entweder die LED defekt oder es besteht ein anderer Fehler, wie z.B. Kurzschluß mit einem anderen Signal bzw. eine Leitungsunterbrechung. Findet man kein korrektes Signal, liegt der Fehler wahrscheinlich beim Machbaustein selbst, der entweder defekt oder falsch programmiert ist. Auch Kontaktprobleme innerhalb seines Sockels können eine fehlerfreie Funktion verhindern. Anpassung des Testprogramms: Das Programm ist an die Größe des verwendeten EPROMS anzupassen. Weiterhin muß berücksichtigt werden, welche Mikroprozessor Architektur (403GA oder 403GCX) benutzt wird. Diese Konfiguration geschieht analog wie in Abschnitt 5.1.2 beschrieben.

56

Implementierung

5.1.4 DRAM Test Diese Tool überprüft das gesamte dynamische RAM, welches sich auf dem Spyder-Core Emulationsboard befindet, auf korrekte Funktion. Im folgenden möchte ich die Arbeitsweise dieses Prüfalgorithmus kurz beschreiben Das Programm verzweigt zunächst zum _reset Vektor, welcher am Beginn des Adreßbereichs des EPROMs liegt. Wird ein 128 kByte EEPROM verwendet, liegt diese Einsprungadresse bei 0xFFFE0000. Zunächst wird das Maschinenstatusregister (MSR) initialisiert, die Caches invalidiert und die Interrupts abgeschaltet. Anschließend erfolgt eine Initialisierung der 8-Bit I/O Bank (siehe auch Abschnitt 5.1.3) sowie der zu untersuchenden DRAM-Bank selbst. Der eigentliche Test läuft wie folgt ab: • Zuerst schreibt das Tool die Testmuster 0xaaaaaaaa und 0x55555555 an die erste Adresse des zu scannenden DRAM-Adreßraums und liest diese Werte zurück. Schlägt dieser Patterntest fehl, so arbeitet der Datenbus nicht korrekt. • Danach wird ein sogenannter Hausnummerntest durchgeführt: Die Adresse einer Speicherstelle wird in selbige geschrieben Danach wird sie wieder ausgelesen und verglichen, ob ihr Inhalt mit dem Wert übereinstimmt, der hineingeschrieben wurde. Auf diese Weise wird der zu testende DRAM-Adreßraum durchgescannt. Dabei werden jeweils erst eine bestimmte Anzahl von Adressen beschrieben und danach ausgelesen. Wurde sowohl der Pattern- als auch der Hausnummerntest erfolgreich durchlaufen, wird die grüne LED eingeschaltet. Der Test beginnt anschließend von vorn und wird beliebig oft wiederholt. Somit sind auch Fehler erfaßbar, welche erst nach längerer Zeit auftreten, sei es durch thermische oder andere Ursachen. Wird irgendwann im Verlauf des Tests ein Fehler erkannt, so wird die rote LED eingeschaltet und das Programm verweilt in dieser Stelle in einer Endlosschleife, d.h. der Testalgorithmus hält. Somit kann z.B. mittels eines Logic Analyzers festgestellt werden, an welcher Adresse der Fehler auftrat und somit Rückschlüsse auf die Ursache gezogen werden (z.B. Kurzschlüsse zwischen Adreßleitungen). Der Programmcode selbst läuft natürlich vollständig im EPROM, so daß das Programm auch bei einem fehlerhaften DRAM Baustein lauffähig bleibt. Auswertung: Ich möchte an einem Beispiel erläutern, auf welche Weise Fehler des DRAM mit Hilfe dieses Diagnosetools erkannt werden können: Nehmen wir an, am Datenbus des Power PC sind die Leitungen DP8 und DP9 kurzgeschlossen. Die Signale DP8 und DP9 des PPC entsprechen den Bits D23 und D22 eines Wortes der Länge von 32 Bit.

57

DP0

DP8

DP9

DP31 Do

D31

Abbildung 5.4 Fehler auf Datenbus

Während des Testprogramms wird DP9 (D22) zum ersten mal benutzt, wenn ein Zugriff auf die Adresse 0x400000 (4 MByte) erfolgt. Wegen des Kurzschlusses kommt es zu einem Fehler. Dieser wird vom Programm erkannt und die rote LED aktiviert. Anpassung des Testprogramms: • Die Konfiguration der Einsprungadresse _reset sowie der Cache Settings erfolgen wie in den vorangegangenen Abschnitten beschrieben. • Weiterhin ist auf die richtigen Settings für die DRAM-Bank zu achten. Diese Einstellungen sowie die Einblendung der DRAM Bank in den Adreßraum des PPC wird in der Datei bcr.inc beschrieben: ; ; ; ; ; ; ; ; ;

Spezifiziere Device Control Register BR7: DRAM 32 Bit Speicherbereich: DRAM Bank7 Simm-Module( 4 MB *32) 16 MB SIMM Address-Range:0x0000_0000 to 0x00FF_FFFF (Region 0) und 0x8000_0000 to 0x80FF_FFFF (Region 16) 31 | 30 29 28 | 27 26 25 24 23 22 21 20 | Address Lines 0 | 1 2 3 | 4 5 6 7 8 9 10 11 | Address Lines PPC x | 0 0 0 | 0 0 0 0 0 0 0 0 | Settings

BAS_7 BS_7 ... SD_7

.equ .equ

0x0 0x4

bd_readyChanged = TRUE; return dev_flash0; } Abbildung 5.29 Erstellen einer Parametertabelle für Block Device Treiber

Filesystem initialisieren und verfügbar machen: Auf dem durch die Parametertabelle beschriebenen Blockdevice kann nun mittels einer Betriebssystemfunktion ein DOS-Filesystem (oder ein anderes Filesystem) erzeugt werden. Dieser Vorgang wird auch als Formatieren bezeichnet. Sowie ein gültiges Filesystem existiert, kann dieses in die Verzeichnisstruktur eingebunden (gemountet) werden. Die folgenden Bespiel zeigen, wie ein Filesystem angelegt und automatisch gemountet wird, bzw. wie man ein bereits existierendes Filesystem einbindet:

// Parameter für Blocktreiber initialisieren: dev_flash0 = flash0_DeviceCreate(anzahl_blocks, ReadWrite); // Formatieren: volume = dosFsMkfs(“/“, dev_flash0);

Abbildung 5.30 Formatieren und automatisch mounten

1. Die angegebenen „Flashadressen“ sind Offsetadressen innerhalb des Adreßraums des Flash-Speichers und keine Adressen im Adreßraum des Mikrocontrollers

88

Implementierung

// Parameter für Blocktreiber initialisieren dev_flash0 = flash0_DeviceCreate(anzahl_blocks, ReadWrite); // Filesystem an das Rootverzeichnis “/“ mounten volume = dosFsDevInit(“/“, dev_flash0, 0);

Abbildung 5.31 Mounten eines existierenden Filesystems

89

5.4.2 DOS Filesystem für DRAM (RAM-Drive) Für das DRAM sind die Block Device Driver bereits in VxWorks integriert. Die Initialisierung kann daher einfacher ausfallen als beim Flash-RAM. Zuerst müssen wiederum die Parameter für den Device Treiber initialisiert werden, danach kann das Filesystem erzeugt bzw. gemountet werden. Block Device Treiber initialisieren: Die Initialisierung erfolgt analog, wie dies im vorangegangenen Abschnitt über den Flash-Speicher beschrieben wurde. Durch den Aufruf der Betriebsystemfunktion ramDevCreate wird die Parametertabelle für den Treiber erstellt. Die Übergabe der Parameter erfolgt in den Argumenten der Funktion: ramDevCreate( rdev_addr, rdev_blksize, rdev_blksPerTrck, rdev_Blocks, rdev_blkSkip)

Argument rdev_addr

Beschreibung Beginn des RAM-Filesystems im Adreßraum des Mikroprozessors

rdev_blksize rdev_blksPerTrck rdev_Blocks rdev_blksSkip

Blockgröße in Byte Anzahl der Blöcke pro Spur Anzahl der Blöcke auf dem Gerät Anzahl der Blöcke, die am Beginn übersprungen werden Tabelle 5.12: Parameter für RAM-Drive

Filesystem initialisieren und verfügbar machen: Das Formatieren und Mounten erfolgt analog, wie in Abschnitt 5.4.1 beschrieben. Im folgenden Beispiel wird eine RAM-Disk der Größe von 200 kByte angelegt. Alle Sektoren befinden sich in einer Spur, die Sektorgröße ist 512 Byte. Das Betriebssystem sucht automatisch eine Adresse, ab der genügend freier Speicher für die Erzeugung der RAM-Disk zur Verfügung steht (rdev_addr = 0). Anschließend wird ein Filesystem erzeugt, welches an das Verzeichnis „/ramdrive“ gemountet wird.

RamDrive = ramDevCreate (0, 512, 400, 400, 0); volume = dosFsMkfs(“/ramdrive“, RamDrive); Abbildung 5.32 Erzeugen und Mounten einer RAM-Disk

90

Implementierung

5.4.3 Gerätetreiber für XC4000 FPGA 5.4.3.1 Treiberintegration unter VxWorks Im Gegensatz zu einem Speichermedium mit Filesystem erfolgt die Kommunikation mit einem FPGA nicht blockorientiert, sondern zeichenweise. Um das FPGA innerhalb der Treiberarchitektur von VxWorks verfügbar zu machen, mußte ein Character- (bzw. Non-Block) Device Treiber implementiert werden. Ein Treiber für ein solches Non-Block Device stellt insgesamt sieben Funktionen bereit: creat(), remove(), open(), close(), read(), write() und ioctl(). Die Implementierung dieser Routinen ist hardwareabhängig. Unter Umständen werden aber nicht alle Funktionen benötigt. Ein Treiber wird installiert, indem die Betriebssystemfunktion iosDrvInstall aufgerufen wird. Dadurch wird der Treiber registriert, d.h. die Adressen der oben genannten I/O Funktionen in entsprechende Systemtabellen eingetragen. Das I/O System von VxWorks liefert für den soeben registrierten Treiber eine sogenannte Device Driver Number zurück.

Abbildung 5.33 Treiberinitialisierung für Non-Block Devices (Quelle:[Wind97])

Es kann vorkommen, daß in einem System mehrere Geräte des gleichen Typs vorhanden sind, z.B. mehrere FPGAs. Die Registrierung des Treibers für diese Geräte erfolgt nur einmal. Um dennoch auf jedes dieser I/O Devices gezielt zugreifen zu können, wird ihnen ein Name gegeben und die Nummer des entsprechenden Gerätetreibers zugeordnet. Dies geschieht durch Aufruf der VxWorks-Funktion iosDevAdd. Ab diesem Zeitfunkt werden alle I/O Funktionen, die sich auf den Namen eines Gerätes beziehen, vom System an die entsprechende Treiberroutine weitergeleitet. In Abbildung 5.34 werden z.B. zwei Geräte unter den Namen „/xx0“ und „/xx1“ registriert. Der zuständige Gerätetreiber hat die Treibernummer zwei und könnte z.B. ein Treiber für serielle Schnittstellen sein. Wird nun z.B. eine Funktion wie fopen(“/xx0“ , “r+“) aufgerufen, so wird dieser Aufruf durch die Routine open() des Schnittstellentreibers abgearbeitet. Die Argumente dev0 und dev1 dienen im übrigen dazu, weitere geräteabhängige Parameter zu spezifizieren. Im Beispiel könnten den Treiberroutinen u.a. die verschiedenen I/O Adressen der beiden seriellen Schnittstellen mitgeteilt werden.

91

Abbildung 5.34 Hinzufügen von Geräten (Quelle:[Wind97])

5.4.3.2 Umsetzung des Konzepts für SPYDER-CORE-P1 Im folgenden möchte ich darstellen, wie ich den Devicetreiber für FPGAs der Serie XC4000 in das Betriebssystem VxWorks eingebunden habe. Um das FPGA durch eine Applikation konfigurieren zu lassen, sind nur die Funktionen zum Öffnen, Beschreiben und Schließen des Gerätes notwendig, so daß ich mich auf die Beschreibung dieser Routinen beschränken möchte. Treiberfunktion Open(): Die Funktion Open() stellt die exklusive Verfügbarkeit des FPGA für die Task sicher, durch welche sie aufgerufen wurde. Dazu allokiert sie ein binäres Semaphore. Dieses Semaphore wird erst durch die Operation Close() wieder freigegeben. Versuch eine andere Task in der Zwischenzeit einen Zugriff auf das FPGA, wird dies abgeweisen und eine entsprechende Fehlermeldung generiert. Nachdem das Semaphore allokiert ist, wird das Spyder-System initialisiert, so daß daraufhin das FPGA konfiguriert werden kann. In Abbildung 5.35 ist dargestellt, wie diese Sequenz verläuft. Das Setzen der dort erwähnten Signale erfolgt wieder durch Schreibzugriffe auf spezielle Adressen im Adreßraum des Mikroprozessors. Ein Mach210A1 analysiert die Aktivitäten auf dem Adreß- und Datenbus und generiert dann die entsprechenden Logikzustände der betroffenen Steuerleitungen. Eine Übersicht über dieses Adressen folgt am Ende dieses Abschnitts.

1. ein programmierbarer Logikchip der Firma AMD

92

Implementierung

Signale SLAVEOUT und PROG aktivieren

Pause

Signal PROG deaktivieren

Pause

Signal PROG wieder aktivieren

Pause Abbildung 5.35 Hardware-Initialisierungssequenz der Funktion Open()

Treiberfunktion Write(): Schreibt eine Applikation ein Zeichen auf das FPGA, so wird vom Betriebssystem die Treiberfunktion Write() aktiviert. Diese Funktion serialisiert das Zeichen und sendet es dann bitweise an das FPGA, wobei das niederwertigste Bit zuerst übertragen wird. Die Übertragung geschieht durch Schreibzugriffe auf das Datenregister des FPGA, welches dem Mikroprozessor über eine Adresse innerhalb seines Speicherraums zugänglich ist. Die Konfiguration ist beendet, wenn das DONE-Signal, welches vom FPGA kommt, aktiv wird.

Senden(DataByte): for Bit=0 to 7 of DataByte { if Bit = 0 then Write(FpgaDataPort, 0)

else Write(FpgaDataPort, 1)

}

Abbildung 5.36 Datenübertragung

93

Treiberfunktion Close: Diese Routine löscht das SLAVEOUT Signal wieder. Nach einer kurzen Wartepause gibt sie weiterhin das Semaphore wieder frei, welches von der Funktion Open() allokiert wurde. Somit steht der FPGA-Gerätetreiber wieder zur Verfügung. Adressen: Zum Abschluß dieser Betrachtungen möchte ich die benutzten Steuersignale kurz bescheiben. Während die oben dargestellten Algorithmen für das Öffnen, Beschreiben und Schließen einer Gerätedatei für den Zugriff auf ein XC4000 Device unter VxWorks unabhängig vom speziellen Boarddesign sind, können sich die Adressen für die Generierung der Steuersignale in zukünftigen Versionen des Spyder-Core Systems ändern.

Signal PROG

Adresse 0xf3000000

Databita 0

Zugriffb w

Beschreibung aktiviert Programmiermode des FPGA

INIT

0xf3000000

r

SLAVEOUT DONE

0xf3000000

1

w

0xf3000000

1

r

DATA

0xf3400000

w

Signal kann zur Fehlererkennung beim Ladevorgang dienen, hier nicht benutzt verbindet das FPGA mit dem System-BUS Signal wird aktiv, wenn der Konfigurationsvorgang erfolgreich abgeschlossen wurde dient der Datenübertragung zum FPGA

Tabelle 5.13: Signale und Adresse für SPYDER-CORE-P1c a. Beim Power PC ist das Datenbit 0 das höchstwertige Bit b. w.. Verhalten bei Schreibzugriff, r.. Verhalten bei Lesezugriff c. Stand: Spyder-Core-P1, Mai 1999

Beispiel: Wenn man das Datenbit 1 auf Adresse 0xf3000000 mit einer binären Eins beschreibt, so aktiviert man das SLAVEOUT-Signal. Führt man auf die gleiche Adresse hingegen einen Lesezugriff aus, so spiegel Bit 0 den aktuellen Zustand des DONE-Signals wider.

94

Implementierung

5.5 Demonstrator: Erweiterer FTP-Server 5.5.1 Konzept Der vorliegende FTP Server verdeutlicht auf anschauliche Weise, welche neuen Möglichkeiten sich durch die Bereitstellung eines Filesystems sowie eines Gerätetreibers für FPGA eröffnen: Datenverwaltung: Der FTP-Server stellt all die Funktionalität bereit, welche benötigt wird, um Daten vom Entwicklungsrechner zum Spyder-Core-Board und zurück zu übertragen. Weiterhin sind fundamentale Filesystemoperationen wie das Anlegen, Löschen sowie Umbenennen von Dateien und Verzeichnissen möglich. Als Speichermedium auf Spyder-Core kommt sowohl ein RAM-Drive als auch das Flash-RAM in Betracht. Für beide Medien steht ein funktionsfähiges Filesystem zur Verfügung (siehe Abschnitt 5.4.1 und Abschnitt 5.4.2). Zugriff auf Geräte: Der FTP-Server läßt sich so konfigurieren, daß Geräte wie FPGAs als Verzeichnisse dargestellt werden. Schreibzugriffe auf diese Verzeichnisse bewirken einen direkten Zugriff auf einen Gerätetreiber. Somit ist es z.B. möglich, ein Design File direkt vom Entwicklungsrechner in das entsprechende FPGA zu laden. Darüber hinaus besteht die Möglichkeit, Konfigurationsfiles, welche im Flash-RAM abgelegt wurden, in das FPGA zu kopieren. Dabei erfolgt die gesamte Steuerung mit einem Standard FTP-Client. Damit ist die Integration des Spyder-Core-System in die Benutzeroberfläche eines Entwicklungsrechners mit einem beliebigen Betriebssystem möglich.

Abbildung 5.37 Integration des Spyder-Core Systems in die Benutzeroberfläche des Hostrechners

95

Abbildung 5.37 zeigt, wie sich das Spyder-Core-P1 System dem Benutzer eines Windows basierten Hostrechners darstellen kann. Das Fenster Images zeigt das Dateisystem des Hosts, während im Window des FTP-Clients1 das Filesystem des Spyder Systems zu erkennen ist. Dabei ist der FTP Server so konfiguriert, daß alle Schreibzugriffe auf den Ordner 4010 an den Devicetreiber des FPGA weitergeleitet werden. Zieht man nun mit der Maus ein Design File, r.B. 4010e.rbt auf den Ordner 4010 im FTP Fenster, so wird automatisch ein FTP Upload gestartet und das FPGA sofort geladen. Durch die ausschließliche Benutzung des FTP-Protokolls bestehen auch keine Einschränkungen bezüglich der Hardware des steuernden Computers. Ebenso ist es möglich, den Rechner, auf welchem die Benutzeroberfläche läuft, und das SPYDER-CORE-P1 System selbst beliebig räumlich zu trennen.

5.5.2 Realisierung Die Einbindung der Gerätetreiber wird durch die Definition spezieller Verzeichnisse ermöglicht, denen der Name eines Gerätetreibers zugeordnet wird. Dies geschieht durch Editieren eines Headerfiles (ftpd.h):

#define SPECIALROOT "/fpga" #define SPECIALDIRS "/fpga/4010:/fpga/4036:/fpga/4010XL" #define DEVICENAMES

"/dev/fpga0: : "

Abbildung 5.38 Assoziation zwischen Verzeichnissen und Gerätetreibern

Beispiel (Abbildung 5.38): Die Pfade /fpga/4010, /fpga/4036 und /fpga/4010XL sind als Schnittstelle für Gerätetreiber reserviert. Diese Verzeichnisse werden immer vom FTP Server gelistet, auch wenn sie sich nicht innerhalb des Verzeichnisses /fpga2 auf dem Datenträger (z.B. Flash-Memory) befinden. Existieren auf dem lokalen Filesystems des Spyderboards Verzeichnisse mit diesen Pfaden, werden sie vom FTP Server ignoriert. In der letzten Zeile erkennt man, wie diesen Pfaden Gerätetreiber zugeordnet werden. So wird dem Verzeichnis /fpga/4010 der Gerätetreiber /dev/fpga0 zugeordnet, der z.B. für das FPGA der Serie XC4000 zuständig ist, welches sich auf Spyder-Core-P1 befindet. Den beiden anderen Pfaden ist kein Treiber zugeordnet. Schreibversuche auf diese Ordner werden mit einer entsprechenden Fehlermeldung zurückgewiesen. Es existieren zwei grundsätzliche Möglichkeiten, ein FPGA oder ein anderes Gerät mit Hilfe des FTP-Servers zu bescheiben: • Upload: Diese Variante kommt immer dann zur Ausführung, wenn eine Datei von einem entfernten Rechner, z.B. dem Entwicklungs-PC, zu einem Gerät auf Spyder-Core übertragen werden soll. Benutzt wird dabei der Befehl STOR des FTP Protokolls, welcher auf einigen textbasierten Clients auch über den Befehl put benutzt wird. 1. zu sehen ist der FTP Client „Voyager“ 2. dieses Verzeichnis muß auf dem Datenträger vorliegen und dient gewissermaßen als Mountpoint

96

Implementierung

• Umbenennen: Liegt eine Datei schon auf einem lokalen Filesystem des Spyder-Systems, so realisiert der FTP-Server dies unter Benutzung der Befehle RNFR und RNTO, welche das FTP-Protokoll anbietet. Diese Funktionen werden auf textbasierten Clients meist unter dem Kommando rename zusammengefaßt. Während die Kommandos bei normalen Dateien tatsächlich nur ein Umbenennen bzw. Verschieben bewirken, ist ihre Arbeitsweise modifiziert, wenn als Ziel ein Verzeichnis gewählt wurde, hinter dem sich ein Gerätetreiber verbirgt. In beiden Fällen untersucht der FTP-Server das Format einer Datei, bevor er sie an das entsprechende Gerät sendet. Auf diese Weise wird auch intern eine Konvertierung der verschiedenen Dateiformate auf ein gerätespezifisches Format realisiert. So konvertiert der FTP-Server z.B. Dateien, welche im Xilinx-Bitstream-ASCII Format (RBT) vorliegen, in ihr Binäräquivalent. Dateien mit unbekanntem Dateiformat werden mit einer entsprechenden Fehlermeldung abgelehnt. Beispiel: Betrachten wir noch einmal Abbildung 5.37. Wenn der Anwender im Fenster des FTP-Clients versucht, die Datei 4010e.rbt mit der Maus in den Ordner 4010 zu ziehen, so geschieht folgendes: • Der Client schickt einen Befehl an den FTP-Server, die Datei 4010e.rbt in den Ordner /fpga/4010 zu verschieben. • Der Server erkennt, daß diesem Ordner ein Gerätetreiber zugeordnet ist • Der FTP-Server erkennt an der Struktur der Datei, daß es ich um ein RBT-File handelt. Er konvertiert es entsprechend. • Die ins Binärformat konvertierten Dateien werden an das FPGA gesendet

97

Kapitel 6

Zusammenfassung und Ausblick Diese Arbeit hatte zwei wesentliche Teile zum Inhalt. Zum einen bestand die Aufgabe, die Produktqualität des existierenden Emulationssystems Spyder-Core-P1 durch Bereitstellung spezifischer Softwarekomponenten zu erhöhen. Diese Komponenten dienen der Qualitätssicherung, Performanceanalyse und der Erhöhung des Bedienkomfort des Spyder-Core-Systems. Die entwickelten Softwarekomponenten haben sich bewährt, so daß sie auch in zukünftigen Projekten des Forschungszentrums Informatik ihren Platz finden werden. Hervorzuheben ist dabei vor allem der Know-How-Zuwachs bei der Entwicklung eingebetteter Systeme. So unterstützt das FZI zur Zeit laufende Projekte sowohl in der Industrie als auch im universitären Bereich, wie z.B. an der Universität München1. Ein weiters Aufgabenfeld war, dem System neue Aufgabenfelder in der aktuellen Forschung zu erschließen. Das Spyder-Core System steht jetzt als Plattform für Untersuchungen auf dem Gebiet der Run Time Rekonfiguration bereit. Zur Zeit existiert ein Treiber für FPGAs der XC4000 Reihe von Xilinx. Der Zeitraum, in dem diese Arbeit enstand, gestattete leider nur eine kurze Bewertung der Leistungsfähigkeit des aktuellen Spyder-Core-P1 Systems bezüglich der Anforderungen, welche durch die Run Time Rekonfiguration gestellt werden. Eine Analyse der Zeitressourcen, welche zum Umkonfigurieren nötig sind, zeigt folgendes Ergebnis:

1. Rekonfiguration von FPGA unter Windows NT

98

Zusammenfassung und Ausblick

Schließen

Öffnen ca. 5 ms

Schreiben

ca. 5 ms

ca. 115 ms

Abbildung 6.1 Performance von Spyder Core P1 bei der Rekonfiguration des FPGA

Wie in Abbildung 6.1 zu erkennen ist, liegt der Zeitbedarf für die Rekonfiguration des FPGA im oberen Millisekundenbereich. Somit eröffnen sich für Spyder-Core all diejenigen Forschungsfelder, in denen es eine komfortable, automatisierbare Rekonfigurierbarkeit der programmierbaren Logik erforderlich ist. Ein sehr interessantes Beispiel aus diesem Bereich ist die Anwendung evolutionärer Algorithmen für den Entwurf von Hardwaredesigns, wie dies z.B. in [AT96] beschrieben wird. Im wesentlichen geht es bei diesem Konzept darum, Hardware nicht mehr auf die klassische Weise zu entwerfen, sondern vielmehr eine evolutionäre Lösung zu finden. Man wählt aus einer Reihe von anfangs zufällig generierten „Schaltungen“ diejenige aus, welche in ihrer Funktionalität dem vorgegebenen Ziel am nächsten kommt. Von dieser werden verschiedene Variationen erstellt, welche ihrerseits wiederum bewertet werden. Mit jedem Zyklus „lernt“ die Hardware hinzu. Auf diese Weise ist es unter Umständen möglich, „automatisch“ Lösungen zu finden oder vereinfacht gesprochen, ein System zu entwickeln, welches sich selbst den gestellten Aufgaben anpaßt. Andere Aufgabenbereiche hingegen stellen wesentlich höhere Anforderungen an die Geschwindigkeit des Systems, insbesondere an die Ladezeiten des FPGA. Darunter zählen z.B. Anwendungen wie ein ATM Monitor, wie er in [KW99] beschrieben wurde. Daher werden neue Technologien wie die Unterstützung der Virtex-Architektur von Xilinx den weiteren Entwicklungsweg des Spyder System prägen. Die komponentenorientierte Struktur (Filesystem, FPGA-Treiber, Anwendungen wie FTP-Server) des Softwarepaketes gestattet es dabei, diese neuen Hardwarekomponenten innerhalb relativ kurzer Zeit auch für die Arbeiten verfügbar zu machen, welche sich mit Problemen der Run Time Rekonfiguration beschäftigen.

99

Anhang A

Referenzen [Act94]

___: FPGA-Data Book and Design Guide. Actel, Inc. 1994

[Army96]

__: Real Time Executive for Multiprocessor Systems. Development Environment Guide. U.S. Army Missile Command, Redstone Arsenal, Alabama 1996

[AT96]

Adrian Thompson: An evolved circuit, intrinsic in silicon, entwined with physics. COGS, University of Sussex, Brighton, BN1 9QH, UK

[Atmel1]

__:Programming Specifications for Atmel’s FPGA Configuration EEPROMS AT17C65/128/256/010 AT17C65A/128A/256A/512A/010A

[CN97]

Carsten Nitsch: Power-PC-Tools, FZI Karlsruhe und Universtät Leipzig 1997

[DeMi95]

G.De Micheli: Hardware/Software Co-Design: Application Domains and Design Technilogies. Proceedings of the NATO Advanced Study Institute on Hardware/ Software Co-Design, Tremezzo Italy, Kluwer Academic Publishers, 1995

[Fu97]

MB86960 Network Interface Controller with Encoder/Decoder (NICE). Data Sheet, Fujitsu 1997

[HaHu95]

J.D. Hadley, B.L.Hutchings: Design Methologies for Partial Reconfigured Systems. Dep. of Electrical and Computer Eng. Brigham Young University Provo, UT 84602, 1995

100

Referenzen

[HePa96]

J. Hennessy, D.Patterson: Computer Architecture - A Quantitative Approach. Second Edition, Morgan Kaufmann Publishers, Inc. SF-CA-USA, 1996

[HM93]

Hans-Peter-Messmer. PC-Hardwarebuch. Addison-Wesley 1993

[HuWi95]

B. Hutchings, M. Wirthlin: Implementation Approaches for Reconfigurable Logic Applications. Proceedings of 5th International Workshop of Field-Programmable Logic and Applications, Oxford 1995

[KW99]

Karlheiz Weiß: Systematische Architekturfindung und Emulation eingebetteter Systeme. Karlsruhe 1999

[MS97]

Mike Schubert: Echtzeitbetriebssysteme. Vortrag an der HTW Dresden, 1997, http://wwwbs.informatik.htw-dresden.de/svortrag/ai94/Schubert/RTFolien.html

[St90]

J. Stankovic, K.Ramamritham: What is predictability for Real-Time Systems, 1990

[St99]

Thorsten Steckstor: Evaluierung der Vorteile des Einsatzes eines Echtzeitbetriebssystems in einem eingebetteten System am Beispiel von ASI. FZI Karlsruhe 1999

[Wind97]

__: Tornado User´s and Programmer´s Guide. Windriver Systems,Inc. 1997

[WirHut97]

M. Wirthlin, B. Hutchings: Improving Functional Density Through Run-Time Constant Propagation. ACM-Symposium on Field Programmable Gate Arrays, 1997.

[Wo94]

W. Wolf: Hardware-Software Co-Design of Embedded Systems. Procceedings of the IEEE, Vol.82, No.7, July 1994

[WSNRo99] K. Weiß, T. Steckstor, C. Nitsch, W. Rosenstiel: Performance Analysis of RealTime-Operation Systems by Emulation of an Embedded System. 10th IEEE International Workshop on Rapid System Prototyping (RSP), Clearwater, Florida, USA, 1999 [Xil99]

___: Virtex 2,5V Field Programmable Gate Arrays. Xilinx, Inc. Datasheet, http:/ /www.xilinx.com, 1999

[Xil98/1]

___: The Programmable Logic Data Book. Xilinx, Inc. 1998

101

Anhang B

Loader /*----------------------------------------------------------------------------Programm Loader ================= Datei: loader21.c laedt Sektionen aus einem ELF-File in dem Speicher des Power PC -----------------------------------------------------------------------------*/

#include #include #include #include #include #include

„defines.h“ „elf.h“ „loader.h“

#ifdef BCC #include #endif

#define VERSION „Loader Version 2.1“ char *SectName[256]; void help(char *s) { fprintf(stderr, „usage fprintf(stderr, „

%s [-s] [-mbig=] \n“, s); -s .. reverse Byteorder \n“);

102

Loader

fprintf(stderr, fprintf(stderr, fprintf(stderr, fprintf(stderr, fprintf(stderr, fprintf(stderr, fprintf(stderr, fprintf(stderr, exit(-1);

„ „ „ „ „ „ „ „

-mbig=1 -mbig=0 -V -v -c -r -d -h , --help

target = big endian\n“); target = little endian\n“); version\n“); verbose\n“); do not initialize ppc\n“); do not start application\n“); baseaddresse (hex)\n“); help\n\n“);

} long GetVal(char *arg, long i, long b) { char *s; if (arg[2] == ‚ ‚) s = arg + i + 1; else s = arg + i; if (s) return strtol(s, NULL, b); else return -1; } void version() { fprintf(stderr, VERSION); fprintf(stderr, „\n\n“); exit(0); } void main(int argc, char **argv) { FILE *f, *g; long ByteOrder = -1; char FileName[256]; char buf[4096], buf1[4096]; long i, x, rsize; long address, size; long base = -1; long verbose = 0; long initppc = 1; long run = 1; Elf32_Ehdr stElfHdr; Elf32_Phdr(*stProgHdrs)[]; Elf32_Phdr(*stSectHdrs)[]; Elf32_Shdr *SecHdr; //StrArray SectName[64]; long found; char foo; if (argc < 2) { help(argv[0]); exit(-1); } else { for (i = 1; i < argc; i++) if ((char *) strstr(argv[i], „-s“) == argv[i]) ByteOrder = REVERSE_BYTE_ORDER; else if ((char *) strstr(argv[i], „-mbig=“) == argv[i]) { printf(„DEBUG: %d\n“, GetVal(argv[i], 6, 10));

103

if (GetVal(argv[i], 6, 10) == 0) ByteOrder = REVERSE_BYTE_ORDER; else ByteOrder = DEFAULT_BYTE_ORDER; } else if (((char *) strstr(argv[i], „-h“) == argv[i]) || ((char *) strstr(argv[i], „--help“) == argv[i])) help(argv[0]); else if ((char *) strstr(argv[i], „-V“)) version(); else if ((char *) strstr(argv[i], „-d“)) base = GetVal(argv[i], 2, 16); else if ((char *) strstr(argv[i], „-v“)) verbose = 1; else if ((char *) strstr(argv[i], „-c“)) initppc = 0; else if ((char *) strstr(argv[i], „-r“)) run = 0; else strcpy(FileName, argv[i]); }

if (base == -1) base = 0xD000;

f = fopen(FileName, „rb“); if (f == NULL) { fprintf(stderr, „/Can not open %s\n“, FileName); perror(„“); exit(-1); } /* Wenn ByteOrder nicht per Option angegeben wurde, automatisch feststellen */ if (ByteOrder == -1) { fseek(f, 5, SEEK_SET); fread(&x, 1, 1, f); if (x == 1) ByteOrder = REVERSE_BYTE_ORDER;/* little */ else ByteOrder = DEFAULT_BYTE_ORDER;/* big */ fseek(f, 0, SEEK_SET); } /* Alle Headerinformationen aus ELF/File lesen */ ReadELFHeaders(f, &stElfHdr, &stProgHdrs, &stSectHdrs, SectName, ByteOrder); if (verbose) { printf(VERSION); printf(„\n\n“); printf(„Byteorder des ELF-Files: „); if (ByteOrder == REVERSE_BYTE_ORDER) printf(„Little Endian\n)“); else printf(„Big Endian\n“); printf(„DualPortRAM-Adresse: 0x%x\n“, base); printf(„\n“); printf(„Analyse des ELF-Files: \n\n“); PrintELFHeaders(stdout, &stElfHdr, &stProgHdrs, &stSectHdrs, SectName); } x = 1;

104

Loader

if (verbose) printf(„Versetze PPC in reset-Zustand\n“); /* Aktiviere ISA Bus */ printf(„ReadChunk(base, 0x807, &foo, 1);\n“); ReadChunk(base, 0x807, &foo, 1); printf(„done\n“); foo &= 0xfe; WriteChunk(base, 0x807, &foo, 1); //WriteRegister(base, REG_GNET, &x);

/* PCC in reset */

foo = 0x00; WriteChunk(base, REG_GNET, &foo, 1); sleep(1);

if (initppc) { if (verbose) printf(„initialisiere PPC-Speicher\n“); for (i = 0; i < 0x800; i++) { buf[i] = 0xaa; buf1[i] = 0x00; } WriteChunk((short) base, 0, (char *) &buf, 0x800); } else if (verbose) printf(„initialisiere PPC-Speicher nicht\n“);

//exit(-1); /* Test, ob die Sektionen .rvector, .init_dp und .copy im File stehen. Falls Option -c (nur kopieren) nicht gesetzt ist, Abbruch mit Fehlermeldung */ found = 0; if (stElfHdr.e_shnum) for (i = 0; i < stElfHdr.e_shnum; i++) { if (!strcmp(SectName[i], „.rvector“)) found |= 0x01; if (!strcmp(SectName[i], „.entry“)) found |= 0x02; if (!strcmp(SectName[i], „.copy“)) found |= 0x04; } if (initppc) { if (!(found & 0x01)) { fprintf(stderr, „Sektion .rvector nicht gefunden.\n“); exit(-1); } if (!(found & 0x02)) { fprintf(stderr, „Sektion .entry nicht gefunden.\n“); exit(-1); } if (!(found & 0x04)) { fprintf(stderr, „Sektion .copy nicht gefunden.\n“); exit(-1); } } /* --------------------------------------------------------------- */

105

/* Falls (initppc==1), so lade die Sektionen .rvector, .init_dp und .copy direkt */

if (stElfHdr.e_shnum) for (i = 0; i < stElfHdr.e_shnum; i++) { if ((!strcmp(SectName[i], „.rvector“)) || (!strcmp(SectName[i], „.entry“)) || (!strcmp(SectName[i], „.copy“)) ) { SecHdr = (Elf32_Shdr *) (*stSectHdrs) + i; printf(„Lade Section %s\n“, SectName[i]); if (verbose) { printf(„Adresse: 0x%lx (0x%lx)\n“, SecHdr->sh_addr, SecHdr->sh_addr - DPROM); printf(„Groesse: 0x%lx\n\n“, SecHdr->sh_size); } fseek(f, SecHdr->sh_offset, SEEK_SET); rsize = fread(buf, 1, SecHdr->sh_size, f); WriteChunk(base, (SecHdr->sh_addr - DPROM), &buf, SecHdr->sh_size); } } /* -------------------------------------------------------------------- */

/* PPC starten */ if (verbose) printf(„PPC beginnt Codeausfuehrung ab Adresse 0xFFFFFFFC\n“); sleep(1); x = 0; WriteRegister(base, MSG_PC, &x);/* MSG_PC=COM_POLL

*/

/* WriteRegister(base, REG_GNET, &x); *//* PPC aus reset holen */

foo = 01; WriteChunk(base, REG_GNET, &foo, 1); /* Alle Sektionen laden, die ausfuehrbaren Code oder initialisierte Daten enthalten. Groesse muss > 0 sein. ( SecHdr->sh_type == SHT_PROGBITS und SecHdr->sh_flags != 0 und SecHdr->sh_size >0 ) Liegt die Zieladresse nicht im DualMapRAM, so wird die Copyroutine benutzt */

if (verbose) { printf(„Copyroutine gestartet.\n Enter fuer weiter\n“); getc(stdin); } if (stElfHdr.e_shnum) for (i = 0; i < stElfHdr.e_shnum; i++) { SecHdr = (Elf32_Shdr *) (*stSectHdrs) + i; if ((SecHdr->sh_type == SHT_PROGBITS) && (SecHdr->sh_flags) &&

106

Loader

(SecHdr->sh_size) && (strcmp(SectName[i], „.rvector“)) && (strcmp(SectName[i], „.init_dp“)) && (strcmp(SectName[i], „.copy“)) ) { printf(„Kopiere Section %s\n“, SectName[i]); if (verbose) { printf(„Adresse: 0x%lx (0x%lx)\n“, SecHdr->sh_addr, SecHdr->sh_addr - DPROM); printf(„Groesse: 0x%lx\n\n“, SecHdr->sh_size); } CopySection(SectName[i], base, &stElfHdr, &stSectHdrs, SectName, f); } } /* ------------------------------------------------------------------------ */

/* Falls run==1 , copyroutine an dieser Stelle beenden

*/

if (verbose) printf(„Kopierroutine beendet\n“); if (run) { x = COMM_STOP; WriteRegister(base, MSG_PC, &x); } fclose(f); /* sleep(3); f = fopen(„MEM.log“,“w“); ReadChunk( base, 0, &buf1, 0x0800); fwrite(buf1, 1, 0x0800, f); fclose(f); */ exit(0); } /*-----------------------------------------------------------------------------Programm Loader --------------Datei: copy.c *-------------------------------------------------------------------------------/ #include #include #include #include #include

„elf.h“ „loader.h“

long CopySection(char *name, short base, Elf32_Ehdr * stElfHdr, Elf32_Shdr(**stSectHdrs)[], char *SectName[], FILE * elf_file) { long i, found;

107

Elf32_Shdr *SecHdr; char buf[1024]; long to_copy, msg; long address, rsize;

if (!stElfHdr->e_shnum) { printf(„Es existieren keine Sections.\n“); exit(-1); } found = 0; for (i = 0; i < stElfHdr->e_shnum; i++) { if (!strcmp(name, SectName[i])) { SecHdr = (Elf32_Shdr *) (*stSectHdrs) + i; found = 1; i = stElfHdr->e_shnum; } } if (!found) { printf(„Keine Section %s gefunden\n“, name); exit(-1); } to_copy = SecHdr->sh_size; address = SecHdr->sh_addr; fseek(elf_file, SecHdr->sh_offset, SEEK_SET); while (to_copy) { if (to_copy >= sizeof(buf))/* Daten schreiben */ rsize = fread(buf, 1, sizeof(buf), elf_file); else rsize = fread(buf, 1, to_copy, elf_file); msg = COMM_POLL; WriteRegister((short) base, (short) MSG_PC, (char *) &msg); //printf („FNORD\n“); WriteChunk(base, 0x0000, (char *) &buf, rsize); /* ADDRESS schreiben */ WriteRegister(base, (short) WriteRegister(base, (short) WriteRegister(base, (short) WriteRegister(base, (short)

ADDRESS ADDRESS ADDRESS ADDRESS

+ + + +

0, 1, 2, 3,

(char (char (char (char

*) *) *) *)

(&address) (&address) (&address) (&address)

/* SIZE schreiben */ WriteRegister(base, (short) SIZE + 0, (char *) (&rsize) + 1); WriteRegister(base, (short) SIZE + 1, (char *) (&rsize) + 0);

+ + + +

msg = 5; WriteRegister(base, (short) MSG_PPC, (char *) &msg); msg = COMM_COPY; WriteRegister(base, (short) MSG_PC, (char *) &msg); do { ReadRegister(base, (short) MSG_PPC, (char *) &msg); } while (msg != COMM_COPY); to_copy -= rsize; address += rsize; } return 0;

3); 2); 1); 0);

108

Loader

} /*----------------------------------------------------------------------------Programm Loader

----------------Datei: comm2.c -------------------------------------------------------------------------------*/

#include #include #include #include

„elf.h“ „loader.h“

#ifdef BCC #include #endif

/** Funktion WriteChunk. Argumente:long base .. Basisadresse im PC Adressraum long offset char *chunk .. puffer mit daten, die zu schreiben sind long chunksize .. Anzahl der zu schreibenden Bytes Rueckgabe: Anzahl der geschriebenen Byte */ #ifndef DOS int WriteChunk(long base, long offset, char *chunk, long chunksize) { FILE *mem; long errorcode, wsize, address; address = (base e_shoff, SEEK_SET);

for (i = 0; i < stElfHdr->e_shnum; i++) { offset = (long) SHDRSZ *i + (long) *stSectHdrs; StructRead( elf_file, (void *) offset, (const char *) STRUCT_SECTHDR_FORMAT, ByteOrder); } for (i = 1; i < stElfHdr->e_shnum; i++) {

115

k = stElfHdr->e_shstrndx;/* HeaderNo fuer */ /* Stringtable */ SecHdr = (Elf32_Shdr *) (*stSectHdrs) + k; StringTableOffset = SecHdr->sh_offset; StringTableSize = SecHdr->sh_size;

SecHdr = (Elf32_Shdr *) (*stSectHdrs) + i; StringTableEntryOffset = SecHdr->sh_name; if ((StringTableEntryOffset >= 0) && (StringTableEntryOffset < StringTableSize)) { fseek(elf_file, StringTableOffset + StringTableEntryOffset, SEEK_SET); c = 1; strcpy(s_temp, „“); for (j = 0; c != 0; j++) { c = getc(elf_file); strncat(s_temp, &c, 1); } s = (char *) malloc(strlen(s_temp) + 1); strcpy(s, s_temp); SectName[i] = s; } else { s = (char *) malloc(7); strcpy(s, „[NULL]“); SectName[i] = s; }

}

} return (NoError); }

/**

Funktion PrintELFHeader. *********************************************** Gibt den Inhalt des ELFheader sowie aller Program- und Sectionheader aus Argumente: elf_logfile .. Filehandle. Ausgabe wird hier gelogt. stElfHdr .. Struct fuer ELF-header stProgHdrs .. Enthaelt alle Programheader stSectHdrs .. Enthaelt alle Sectionheader SectName .. Anthaelt Namen der Sections

***************************************************************************/

long PrintELFHeaders(FILE * elf_logfile, Elf32_Ehdr * stElfHdr, Elf32_Phdr(**stProgHdrs)[], Elf32_Shdr(**stSectHdrs)[],

116

Loader

char *SectName[] ) { long i; Elf32_Shdr *SecHdr; Elf32_Phdr *PrgHdr; fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile, fprintf(elf_logfile,

„ELF-Header \n\n“); „ e_ident: %c“, stElfHdr->e_ident[0]); „%c“, stElfHdr->e_ident[1]); „%c“, stElfHdr->e_ident[2]); „%c“, stElfHdr->e_ident[3]); „ %X“, stElfHdr->e_ident[4]); „ %X“, stElfHdr->e_ident[5]); „ %X“, stElfHdr->e_ident[6]); „ %09X\n“, stElfHdr->e_ident[7]); „ e_type: 0x%X \n“, stElfHdr->e_type); „ e_machine: 0x%X \n“, stElfHdr->e_machine); „ e_version: 0x%lX \n“, stElfHdr->e_version); „ e_entry: 0x%lX \n“, stElfHdr->e_entry); „ e_phoff: 0x%lX \n“, stElfHdr->e_phoff); „ e_shoff: 0x%lX \n“, stElfHdr->e_shoff); „ e_flags: 0x%lX \n“, stElfHdr->e_flags); „ e_ehsize: 0x%X \n“, stElfHdr->e_ehsize); „ e_phentsize: 0x%X \n“, stElfHdr->e_phentsize); „ e_phnum: 0x%X \n“, stElfHdr->e_phnum); „ e_shentsize: 0x%X \n“, stElfHdr->e_shentsize); „ e_shnum: 0x%X \n“, stElfHdr->e_shnum); „ e_shstrndx: 0x%X \n\n\n“, stElfHdr->e_shstrndx);

if (stElfHdr->e_phnum) { for (i = 0; i < stElfHdr->e_phnum; i++) { PrgHdr = (Elf32_Phdr *) (*stProgHdrs) + i; fprintf(elf_logfile, „Program Header %d \n\n“, i); fprintf(elf_logfile, „ p_type: 0x%lX \n“, PrgHdr->p_type); fprintf(elf_logfile, „ p_offset: 0x%lX \n“, PrgHdr->p_offset); fprintf(elf_logfile, „ p_vaddr: 0x%lX \n“, PrgHdr->p_vaddr); fprintf(elf_logfile, „ p_paddr: 0x%lX \n“, PrgHdr->p_paddr); fprintf(elf_logfile, „ p_filesz: 0x%lX \n“, PrgHdr->p_filesz); fprintf(elf_logfile, „ p_memsz: 0x%lX \n“, PrgHdr->p_memsz); fprintf(elf_logfile, „p_flags: 0x%lX (4=r 2=w 1=x)\n“, PrgHdr->p_flags); fprintf(elf_logfile, „ p_align: 0x%lX \n\n\n“, PrgHdr->p_align); } } if (stElfHdr->e_shnum) { for (i = 0; i < stElfHdr->e_shnum; i++) { SecHdr = (Elf32_Shdr *) (*stSectHdrs) + i; fprintf(elf_logfile, „Section Header %d \n\n“, i); fprintf(elf_logfile, „ sh_name: %s \n“, SectName[i]); fprintf(elf_logfile, „ sh_type: 0x%lX (1=Code, 2=symtab, 3=strtab, 8=uninitial.Daten)\n“ ,SecHdr->sh_type); fprintf(elf_logfile, „ sh_flags: 0x%lX (4=execinstr 2=alloc 1=write)\n“, SecHdr->sh_flags); fprintf(elf_logfile, „ sh_addr: 0x%lX \n“, SecHdr->sh_addr); fprintf(elf_logfile, „ sh_offset: 0x%lX \n“, SecHdr->sh_offset); fprintf(elf_logfile, „ sh_size: 0x%lX \n“, SecHdr->sh_size); fprintf(elf_logfile, „ sh_link: 0x%lX \n“, SecHdr->sh_link); fprintf(elf_logfile, „ sh_info: 0x%lX \n“, SecHdr->sh_info); fprintf(elf_logfile, „ sh_addralign: 0x%lX \n“, SecHdr->sh_addralign);

117

fprintf(elf_logfile, „ SecHdr->sh_entsize); } } return (NoError);

sh_entsize:

0x%lX \n\n\n“,

} /*------------------------------------------------------------------------Programm Loader --------------Datei: defines.h -------------------------------------------------------------------------------*/ #define BIG 0 #define LITTLE 1

/*------------------------------------------------------------------------Programm Loader -------------Dateien elf.h, elftypes.h

Headerdateien des DIAB Crosscompilers ---------------------------------------------------------------------------*/

118

Loader

/*------------------------------------------------------------------------Programm Loader -------------Dateien elf.h, elftypes.h

siehe Headerdateien des DIAB Crosscompilers ---------------------------------------------------------------------------*/

/*---------------------------------------------------------------Programm Loader -------------Datei: Makefile ---------------------------------------------------------------------------*/

######################## Makefile fuer Loader ########################## # # Zielplattform: Linux Box # ######################################################################### INC=../../include BINDIR=../../bin/linux BIN= loader21

all:

$(BIN) rm_obj

loader21:loader21.o readelf.o comm2.o copy.o if [ ! -d $(BINDIR) ] ; then mkdir $(BINDIR); fi $(CC) loader21.o readelf.o comm2.o copy.o -o $(BINDIR)/loader21 rm_obj: rm -f *.o

loader21.o: loader21.c $(CC) -I$(INC) -DHOST_BYTE_ORDER=$(HOST_BYTE_ORDER) -g -c loader21.c readelf.o: readelf.c $(CC) -I$(INC) -DHOST_BYTE_ORDER=$(HOST_BYTE_ORDER) -g -c readelf.c

comm2.o: comm2.c $(CC) -I$(INC) -DHOST_BYTE_ORDER=$(HOST_BYTE_ORDER) -g -c comm2.c copy.o: copy.c $(CC) -I$(INC) -DHOST_BYTE_ORDER=$(HOST_BYTE_ORDER) -g -c copy.c

clean: rm -f *.o for I in $(BIN) ; do rm -f $(BINDIR)/$$I; done;

119

Anhang C

Benchmarks Messung der Interrupt-Umschaltzeit

/*--------------------------------------------------------------------------

Messung der Interrupt-Umschaltzeit ================================== Datei: init.c Autor: RTEMS Portierung:

Thorsten Steckstor (VxWorks Version) Carsten Nitsch

---------------------------------------------------------------------------*/

#define TEST_INIT #include „system.h“ #include „RegalServer.h“ unsigned int Counter = 0; struct char char char };

slave { adresse; daten_in; daten_out;

struct slave SlaveListe[MAX_SLAVE]; struct sensoraktor { unsigned char slave_listen_nummer;

120

Benchmarks

char bit_positions_maske; };

static static static static

char char char char

MWHighByte; MWLowByte; MRHighByte; MRLowByte;

static char NormalBetrieb; static unsigned char AktuelleSlaveNummer; struct struct struct struct struct struct

sensoraktor sensoraktor sensoraktor sensoraktor sensoraktor sensoraktor

Lichttaster; LichtschrankeL; LichtschrankeR; XPosition[10]; ZPosition[10]; YPosition[3];

struct struct struct struct struct struct struct struct struct struct struct

sensoraktor sensoraktor sensoraktor sensoraktor sensoraktor sensoraktor sensoraktor sensoraktor sensoraktor sensoraktor sensoraktor

MotorXRechts; MotorXLinks; MotorXLangsam; MotorYVorne; MotorYHinten; MotorZOben; MotorZUnten; MotorEALinksAus; MotorEALinksEin; MotorEARechtsAus; MotorEARechtsEin;

/*-----------------------------------------------------------------------*/ /* rtems stuff */ rtems_name sensorSemName; rtems_id sensorSem; unsigned char sysInByte( unsigned char *addr) { return *addr; }

void sysOutByte( unsigned char *addr, unsigned char val) { *addr = val; }

/*----------------------------------------------------------------------*/ /* The ISR */ rtems_isr fpga_isr(rtems_vector_number vector) { unsigned char *led = (unsigned char*) 0xf2c00000; unsigned char intTest; static char sensoren; register unsigned int exisr; register unsigned int exier;

121

*led = 0xff; intTest = sysInByte( (unsigned char*) FPGABaseAdr + IntStatusAdr); if ((intTest & ChannelBit) == 0) { MRLowByte = sysInByte((unsigned char*)(FPGABaseAdr + ChannelNumber*2)); MRHighByte = sysInByte((unsigned char*)(FPGABaseAdr + ChannelNumber*2+1)); sysOutByte( (unsigned char*) (FPGABaseAdr + IntStatusAdr) , ChannelBit); sensoren = SlaveListe[AktuelleSlaveNummer].daten_in; SlaveListe[AktuelleSlaveNummer].daten_in = MRLowByte; rtems_semaphore_release(sensorSem); if (AktuelleSlaveNummer == (MAX_SLAVE-1)) AktuelleSlaveNummer = 0; else AktuelleSlaveNummer++; MWLowByte = 0x80 | SlaveListe[AktuelleSlaveNummer].adresse; MWHighByte = SlaveListe[AktuelleSlaveNummer].daten_out; sysOutByte((unsigned char*) (FPGABaseAdr + ChannelNumber*2), MWLowByte); sysOutByte((unsigned char*) (FPGABaseAdr + ChannelNumber*2+1),MWHighByte); } /* clear external int pin 3 */ exisr |= 0x00000002; asm volatile („mtexisr %0“ :: „r“ (exisr) ); *led = 0x00; /* switch off led */ }

/*----------------------------------------------------------------------*/ /* RTEMS Entry Routine - the ‚main‘ function */ rtems_task Init( rtems_task_argument ignored) { rtems_isr_entry previous_isr; rtems_status_code status; unsigned int i_cache_reg, d_cache_reg, pvr; register unsigned int exisr, exier, iocr; unsigned int *led = (unsigned int *) 0xf2c00000; unsigned char c; *led = 0; printf(„\n“); printf(„******************** Interrupt Test printf(„\n“);

************************\n“);

/* create a semaphore */ sensorSemName = rtems_build_name( ‚S‘, ‚N‘, ‚1‘, ‚ ‚ ); status = rtems_semaphore_create(sensorSemName, 0, RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 1, &sensorSem); /* read system registers */ asm volatile („mficcr %0“ asm volatile („mfdccr %0“ asm volatile („mfexisr %0“ asm volatile („mfexier %0“ asm volatile („mfiocr %0“

: : : : :

„=r“ „=r“ „=r“ „=r“ „=r“

(i_cache_reg) ); (d_cache_reg) ); (exisr) ); (exier) ); (iocr) );

122

Benchmarks

asm volatile („mfspr %0, 0x11f“ : „=r“ ((pvr printf(„ICCR: printf(„DCCR: printf(„processor version: printf(„External Int. Enable Register: printf(„External Int. Status Register: printf(„I/O Control Register: printf(„\n“);

%08x\n“, %08x\n“, %08x\n“, %08x\n“, %08x\n“, %08x\n“,

))); i_cache_reg); d_cache_reg); pvr); exier); exisr); iocr);

/* Catch a externeal Interrupt */ status = rtems_interrupt_catch(fpga_isr, PPC_IRQ_EXTERNAL , &previous_isr); switch( status) { case RTEMS_SUCCESSFUL: printf(„ISR installed successfuly\n“); break; case RTEMS_INVALID_NUMBER: printf(„illegal vector number\n“); case RTEMS_INVALID_ADDRESS: printf(„illegal ISr entry point\n“); default: printf(„Unknown error\n“); } /* program the I/O Control Register iocr = 0x3c000003; /* external Intr. Pin 3: /* external Intr. Pin 1: /* external Intr. Pin 2:

*/ low level */ pos. edge */ pos edge */

asm volatile („mtiocr %0“ :: „r“ (iocr) );

/* program the Interrupt status Register */ exisr = 0xffffffff; /* clear all pending interrupts */ asm volatile („mtexisr %0“ :: „r“ (exisr) );

/* read exisr and exier and print it out */ asm volatile („mfexisr %0“ : „=r“ (exisr) ); asm volatile („mfexier %0“ : „=r“ (exier) ); printf(„External Int. Enable Register: %08x\n“, exier); printf(„External Int. Status Register: %08x\n“, exisr); c = sysInByte( (unsigned char*) (FPGABaseAdr + DeviceIDAdr) ); printf(„DeviceID: 0x%02x\n“, c); sysOutByte( (unsigned char*) (FPGABaseAdr + ResRegAdr), ChannelBit); c = sysInByte( (unsigned char*) FPGABaseAdr + IntStatusAdr); printf(„IntStatus: 0x%02x\n“, c); sysOutByte( (unsigned char*) (FPGABaseAdr + ChannelNumber*2), 0); sysOutByte( (unsigned char*) (FPGABaseAdr + ChannelNumber*2+1), 0);

/* register the ISR */ status = rtems_interrupt_catch(fpga_isr, PPC_IRQ_EXTERNAL , &previous_isr); switch( status) { case RTEMS_SUCCESSFUL: printf(„ISR installed successfuly\n“); break; case RTEMS_INVALID_NUMBER: printf(„illegal vector number\n“); case RTEMS_INVALID_ADDRESS: printf(„illegal ISr entry point\n“); default: printf(„Unknown error\n“); } exier = 0x02; /* enable external int pin 3 */ asm volatile („mtexier %0“ :: „r“ (exier) ); printf(„waiting\n“); while(1)

123

{ rtems_semaphore_obtain(sensorSem, RTEMS_DEFAULT_OPTIONS, RTEMS_NO_TIMEOUT); asm volatile („mfexisr %0“ asm volatile („mfexier %0“

: „=r“ (exisr) ); : „=r“ (exier) );

}

}

/*--------------------------------------------------------------------------

Messung der Interrupt-Umschaltzeit ================================== Datei: RegalServer.h Autor: Thorsten Steckstor

---------------------------------------------------------------------------*/

/* RegalServer.h - header used by RegalServer */ /* defines */ #define #define #define #define #define

SERVER_PORT_NUM SERVER_WORK_PRIORITY ERROR_WORK_PRIORITY SERVER_STACK_SIZE SERVER_MAX_CONNECTIONS

#define REQUEST_MSG_SIZE /* Y coordinates */ #define INOUT_STATION #define MITTE #define HOCHREGAL /* Error Codes */ #define SUCESSFUL #define ZuwenigSlaves #define KeinYTaster #define ZweiYTaster #define ZweiXTaster #define ZweiZTaster /* ASI Error Masken */ #define GeneralError #define FatalError #define ASIPowerFail #define Manchester #define Stopbit #define Timeout #define MSPause 0x04 #define Parity

0xFF 0x00 0x01

0x00 0x40 0x41 0x42 0x43 0x44

0x80 0x40 0x20 0x10 0x01 0x02 0x08

4321 198 199 2000 1

/* /* /* /* /*

server‘s port number for bind() priority of server‘s work task priority of server‘s work task stack size of server‘s work task max clients connected at a time

20

/* max size of request message

*/ */ */ */ */ */

124

Benchmarks

/* Debug Codes */ #define DEBUG 0x00 #define TEST 0x00 /* LED Adressen */ #define LED_OFF_ADR #define LED_INT_ADR

0xF2800000 0xF2C00000

/* Wartezeiten */ #define PICResetTime #define SlaveResetTime #define SteuerTime #define ASIErrorTime /* FPGA #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define

Adressen */ FPGA_INT_LEVEL FPGABaseAdr Pipe1LowByteAdr Pipe1HighByteAdr Pipe2LowByteAdr Pipe2HighByteAdr Pipe3LowByteAdr Pipe3HighByteAdr Pipe4LowByteAdr Pipe4HighByteAdr ResRegAdr LedReg1Adr PSRegAdr LedReg2Adr IntStatusAdr LedReg3Adr DeviceIDAdr LedReg4Adr

15 15 55 60

/* ASI Configuration */ /* #define ChannelNumber #define ChannelBit */ #define ChannelNumber #define ChannelBit #define MAX_SLAVE

30 0xF2000000 0x00000000 0x00000001 0x00000002 0x00000003 0x00000004 0x00000005 0x00000006 0x00000007 0x00000008 0x00000009 0x0000000A 0x0000000B 0x0000000C 0x0000000D 0x0000000E 0x0000000F

0x00000003 0x08

0x00000000 0x01 7

/* ASI-Slave Configuration */ /* #define ID_CODE0x00 #define IO_CODE0x07 */ #define ID_CODE0x0f #define IO_CODE0x07

/*--------------------------------------------------------------------------

Messung der Interrupt-Umschaltzeit ==================================

125

Datei: system.h

---------------------------------------------------------------------------*/

/* * * * * * * * * * * * * * */

system.h This include file contains information that is included in every function in the test set. COPYRIGHT (c) 1989, 1990, 1991, 1992, 1993, 1994. On-Line Applications Research Corporation (OAR). All rights assigned to U.S. Government, 1994. This material may be reproduced by or for the U.S. Government pursuant to the copyright license under the clause at DFARS 252.227-7013. This notice must appear in all copies of this file and its derivatives. system.h,v 1.7 1996/05/29 22:47:39 joel Exp

#include

/* functions ========= */ rtems_task Init( rtems_task_argument argument ); rtems_task Test_task( rtems_task_argument argument );

/*---------------------------------------------------------------------configuration information ========================= */ #define CONFIGURE_SPTEST // #define CONFIGURE_INIT #define CONFIGURE_TEST_NEEDS_CONSOLE_DRIVER // #define CONFIGURE_TEST_NEEDS_CLOCK_DRIVER // #define CONFIGURE_TEST_NEEDS_TIMER_DRIVER #define CONFIGURE_RTEMS_INIT_TASKS_TABLE #include

/*---------------------------------------------------------------------global variables

126

Benchmarks

================= */ TEST_EXTERN rtems_id Task_id[ 4 ]; TEST_EXTERN rtems_name Task_name[ 4 ];

/* array of task ids */ /* array of task names */

127

/*--------------------------------------------------------------------------

Messung der Interrupt-Umschaltzeit ================================== Datei: Makefile (fuer Power PC 403-GCX, beide Caches inaktiv)

---------------------------------------------------------------------------*/

HOMEDIR = /root RTEMS_GNUTOOLS = /usr/local/powerpc-eabi RTEMS_HOME = /usr/local/emb-tools/target/rtems/GCX/ND-NI/rtems/c/ppc LIBRARIES = /usr/local/emb-tools/target/libs MY_LD = $(RTEMS_GNUTOOLS)/bin/ld MY_AS = $(RTEMS_GNUTOOLS)/bin/as MY_CC = $(RTEMS_GNUTOOLS)/bin/gcc MY_STDLIB = $(LIBRARIES)/stdlib.a MY_LIBGCC = $(LIBRARIES)/libgcc2.a MY_LIBC = $(LIBRARIES)/libc.a

MY_LDFLAGS = -T ./system/linkcmds -o Int.GCX-ND-NI.elf -u atexit -u __vectors -u download_entry -u _wrapup_reent

\ \ \ \ \

MY_CFLAGS = -B$(RTEMS_GNUTOOLS)/bin/ \ -mcpu=403 \ -mbig \ -Wall \ -ansi \ -fasm \ -O4 \ -g \ -fno-keep-inline-functions \ -DPPC_ABI=PPC_ABI_EABI \ -DPPC_ASM=PPC_ASM_ELF \ -DRTEMS_NEWLIB \ -DMALLOC_PROVIDED \ -DPPC_VECTOR_FILE_BASE=0x0100 \ -I$(RTEMS_HOME)/include \ -I/usr/local/powerpc-eabi/include \ -I../include \ -I./ \ -msoft-float -mcpu=403 -mnew-mnemonics -mbig \ -c MY_CFLAGS_ASM = -B$(RTEMS_GNUTOOLS)/bin/ \ -mcpu=403 \ -mbig \ -Wall \ -ansi \ -fasm \ -O4 \

128

Benchmarks

-g \ -fno-keep-inline-functions \ -DPPC_ABI=PPC_ABI_EABI \ -DPPC_ASM=PPC_ASM_ELF \ -DRTEMS_NEWLIB \ -DMALLOC_PROVIDED \ -DPPC_VECTOR_FILE_BASE=0x0100 \ -I$(RTEMS_HOME)/include \ -I/tools/gcc-ppc/include \ -I../include \ -I./ \ -msoft-float -mcpu=403 -mnew-mnemonics -mbig \ -S MY_CFLAGS2 = -B$(RTEMS_GNUTOOLS)/bin/ \ -mcpu=403 \ -mbig \ -Wall \ -ansi \ -fasm \ -O4 \ -g \ -fno-keep-inline-functions \ -DPPC_ABI=PPC_ABI_EABI \ -DPPC_ASM=PPC_ASM_ELF \ -DRTEMS_NEWLIB \ -DMALLOC_PROVIDED \ -DPPC_VECTOR_FILE_BASE=0x0100 \ -I$(RTEMS_HOME)/include \ -I/tools/gcc-ppc/include \ -I../include \ -I ./ \ -msoft-float -mcpu=403 -mnew-mnemonics -mbig \ -c

MY_CPPFLAGS = -DRTEMS_NEWLIB \ -DMALLOC_PROVIDED \ -DPPC_ABI=PPC_ABI_EABI \ -DPPC_ASM=PPC_ASM_ELF \ -DPPC_VECTOR_FILE_BASE=0x0100 \ -I$(RTEMS_HOME)/include \ -I/usr/include \ -I../include \

MY_OBJS = ./init.o \ $(RTEMS_HOME)/lib/no-dpmem.rel $(RTEMS_HOME)/lib/debug.rel \ $(RTEMS_HOME)/lib/no-event.rel $(RTEMS_HOME)/lib/no-msg.rel $(RTEMS_HOME)/lib/no-mp.rel $(RTEMS_HOME)/lib/no-part.rel $(RTEMS_HOME)/lib/no-signal.rel $(RTEMS_HOME)/lib/no-timer.rel $(RTEMS_HOME)/lib/no-rtmon.rel $(RTEMS_HOME)/lib/libbsp.a $(RTEMS_HOME)/lib/libcsupport.a $(RTEMS_HOME)/lib/librtems.a $(RTEMS_HOME)/lib/libmisc.a $(MY_LIBC) \ $(MY_STDLIB) \ $(MY_LIBC) \

\ \ \ \ \ \ \ \ \ \ \ \

129

$(MY_LIBGCC)

all:MkDir init.o $(MY_LD) $(MY_LDFLAGS) $(MY_OBJS) MkDir: if [ ! -d o-ppc ] ; then mkdir o-ppc ; fi

init.o: init.c $(MY_CC) $(MY_CFLAGS) init.c -o init.o

## clean: rm -f *.o o-ppc/*

130

Benchmarks

Messung der Task-Umschaltzeiten

/*-------------------------------------------------------------------------Messung der Task-Umschaltzeiten =============================== Dateien:

system.h Makefile siehe „Messung der Interrupt-Umschaltzeiten“ ---------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------Messung der Task-Umschaltzeiten =============================== Datei: init.c Autor: Thorsten Steckstor (VxWorks Version) RTEMS Portierung: Carsten Nitsch ---------------------------------------------------------------------------*/ #define LOOP 1000000 #define TEST_INIT #include „system.h“ #include rtems_task Task1 (); rtems_task Task2 (); /* Global data declaration */ rtems_status_code status; char buffer[256]; rtems_name syncSem1Name; rtems_name syncSem2Name; rtems_id syncSem1; rtems_id syncSem2;

rtems_task task1( rtems_task_argument ignored) { int loopy1; unsigned char *LED1 = (unsigned char*) 0xf2800000; unsigned char *dummy = (unsigned char*) 0xf2000009; while(1) { rtems_semaphore_release(syncSem1); *LED1 = 0x00 ; rtems_semaphore_obtain(syncSem2, RTEMS_DEFAULT_OPTIONS, RTEMS_NO_TIMEOUT ); *LED1 = 0xff;

/* LED1 off

*/

/* LED1 on

*/

131

for(loopy1 = 0; loopy1 < LOOP; loopy1++) dummy = 0x00;

/* dummy I/O */

} }

rtems_task task2(rtems_task_argument argument) { int loopy2; unsigned char *LED2 = (unsigned char*) 0xf2C00000; unsigned char *dummy = (unsigned char*) 0xf2000009; while(1) { *LED2 = 0x00; rtems_semaphore_obtain(syncSem1, RTEMS_DEFAULT_OPTIONS, RTEMS_NO_TIMEOUT ); *LED2 = 0xff; for(loopy2 = 0; loopy2 < LOOP; loopy2++) *dummy = 0x00;

/* LED2 off

*/

/* LED2 on

*/

/* dummy I/O */ rtems_semaphore_release(syncSem2); } }

/*-----------------------------------------------------------------------*/ /* RTEMS ‚main‘ function */ rtems_task Init(rtems_task_argument argument) { unsigned int i_cache_reg, d_cache_reg, pvr;

/* read system registers and print it out */ asm volatile („mficcr %0“ : „=r“ (i_cache_reg) ); asm volatile („mfdccr %0“ : „=r“ (d_cache_reg) ); asm volatile („mfspr %0, 0x11f“ : „=r“ ((pvr ))); printf(„ICCR: %08x\n“, i_cache_reg); printf(„DCCR: %08x\n“, d_cache_reg); printf(„processor version: %08x\n“, pvr); printf(„\n“);

printf(„\n*** Task Switch Test

***\n\n“ );

/* create task names */ Task_name[ 1 ] = rtems_build_name( ‚T‘, ‚W‘, ‚1‘, ‚ ‚ ); Task_name[ 2 ] = rtems_build_name( ‚T‘, ‚W‘, ‚2‘, ‚ ‚ );

/* create semaphore names */ syncSem1Name = rtems_build_name( ‚S‘, ‚N‘, ‚1‘, ‚ ‚ ); syncSem2Name = rtems_build_name( ‚S‘, ‚N‘, ‚2‘, ‚ ‚ );

/* Create 2 Tasks */ status = rtems_task_create(

132

Benchmarks

Task_name[ 1 ], 1, RTEMS_MINIMUM_STACK_SIZE, RTEMS_PREEMPT, RTEMS_DEFAULT_ATTRIBUTES, &Task_id[ 1 ] ); status = rtems_task_create( Task_name[ 2 ], 1, RTEMS_MINIMUM_STACK_SIZE, RTEMS_PREEMPT, RTEMS_DEFAULT_ATTRIBUTES, &Task_id[ 2 ] ); /* Start the tasks */ printf(„Start Tasks\n“); status = rtems_task_start( Task_id[ 1 ], task1, 1 ); status = rtems_task_start( Task_id[ 2 ], task2, 2 );

/* Create 2 Semaphores */ status = rtems_semaphore_create(syncSem1Name, 0, RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 1, &syncSem1); status = rtems_semaphore_create(syncSem2Name, 0, RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 1, &syncSem2);

status = rtems_task_delete( RTEMS_SELF ); }

133

Anhang D

Ladeprogramm für EEPROM

/*------------------------------------------------------------------------Programm eload ================

Datei: eload.c

mit main() Routine

-------------------------------------------------------------------------*/ #include #include #include #include #include #include

„eload.h“

#define DEBUG // Prototypen fuer Funktionen int wait(int); void copy2EEPROM(unsigned int, unsigned char *, unsigned long, unsigned int); void copy2LargeEEPROM(unsigned int, unsigned char *, unsigned long, unsigned int); struct Parameter *GetArgs(int, char **); FILE *OpenOrDie(char *, char *); void WriteAddressByte(unsigned int, unsigned char); void WriteDataByte(unsigned int, unsigned char); struct FPGAData *rbt2bin(FILE *, unsigned long, unsigned long); struct FPGAData *mcs2bin(FILE *, unsigned long, unsigned long); unsigned long GetSize(struct FPGAData *);

134

Ladeprogramm für EEPROM

int AskUser(char *); void UploadRBT(FILE *, struct Parameter *); void UploadMCS(FILE *, struct Parameter *); unsigned long RBTDataSize(FILE * f); unsigned long MCSDataSize(FILE * f);

/************************ Serieller Bitstrom ***************************** Makros fuer die Erzeugung eines seriellen Datagramms fuer die Programmierung des EEPROM Durch Schreibzugriffe auf die Adresse :0x800 werden vom Mach die Signale auf der DATA und CLOCK Leitung jeweils auf eins bzw. 0 gesetzt: Datenbit 0 steuert DATA, Datenbit 1 beeinflusst CLOCK

Timing: Ein Bit:

_________ 72..73 usec: ______| |_______ _____ ________| |_________ | | 18 usec

|

Data ( einsbit )

clock

|

18 usec

| | 36 usec

Ein Byte besteht aus 9 bit ( 8 data, 1 ack), nanach folgen ca 110 usec pause. Ein Byte braucht also ca. 9*72 + 110 = 760 usec ( +/- 10 usec ) Eine Page besteht aus 64 Daten Byte, einem Initbyte und 2 Adressbyte, braucht also fuer die Uebertragung ca 67 * 760 usec = 51 ms (+/- 1 ms), danach folgen hier 20 ms pause. eine page braucht also insgesamt ca 70 ms. DOS: ---

_______ Ein Bit:

72..73 usec:

______|

|_______

_____ ________| |_________ | | 8.25 usec |

|

Data ( einsbit )

clock

|

8.25 usec |

16.5 usec insgesamt also ca 33 usec nach jedem Byte ca. 93 usec Pause ***************************************************************************/

135

#define STARTBIT(BASE) pokeb( BASE, 0x800, 0x1); \ wait(1); \ pokeb( BASE, 0x800, 0x3); \ wait(1); \ pokeb( BASE, 0x800, 0x2); \ wait(1); \ pokeb( BASE, 0x800, 0x0); \ wait(1)

#define STOPBIT(BASE) pokeb( BASE, 0x800, 0x0); \ wait(1); \ pokeb( BASE, 0x800, 0x2); \ wait(1); \ pokeb( BASE, 0x800, 0x3); \ wait(1); \ pokeb( BASE, 0x800, 0x1); \ wait(1) #define EINSBIT(BASE) pokeb( BASE, 0x800, 0x1); \ wait(1); \ pokeb( BASE, 0x800, 0x3); \ wait(1); wait(1); \ pokeb( BASE, 0x800, 0x1); \ wait(1)

#define NULLBIT(BASE) pokeb( BASE, 0x800, 0x0); \ wait(1); \ pokeb( BASE, 0x800, 0x2); \ wait(1); wait(1); \ pokeb( BASE, 0x800, 0x0); \ wait(1)

/*************************** Programmierung des Timers ******************* * * Timer-Baustein 8253 : 1/18.21/65536 s = 1/1193180 s ( 0.83 usec ) * * Ein Tick dauert ca. 0.83 usec * * wait wartet fuer 3*x Ticks. Unter DOS vergehen in dieser Zeit rund * 3..4 usec, unter Windows duch Overhead entpreched mehr Zeit. * * Somit sollte nach Setzen der Daten/Clocksignale innerhalb von Makros wie * EINSBIT oder STOPBIT ein wait(1) stets eine hinreichend lange Pause * bewirken, damit das EEPROM die Signale verarbeiten kann. * Zum Schreiben einer Page (64 Byte bzw 128 Byte) ist entsprechend laenger * zu warten. **************************************************************************/ int wait(int x) { unsigned int t0, t1; int dt; unsigned int foo; int xx = (int) 3 * x; foo = inportb(0x40);

136

Ladeprogramm für EEPROM

t0 = (inportb(0x40) > 8; STARTBIT(base); if (chip) WriteAddressByte(base, 0xae);// DeviceByte: 1 0 1 0 else // ^ ^ WriteAddressByte(base, 0xa6);//

x 1 1 0 [chip] [r/w]

WriteAddressByte(base, adr_hi);// Adresse, hier 16 bit lang WriteAddressByte(base, adr_lo); for (i = 0; i < pagesize; i++)// 64 Byte uebertragen WriteDataByte(base, buf[i]); STOPBIT(base); delay(20);// Pause fuer Internen Schreibzyklus }

// // // // // // // // // // //

Routine fuer 17C512 und 17C010 page ist die Nummer der Uebertragenen Page und bestimmt unmittelbar die Adresse im EEPROM, ab welcher diese page abgelegt wird chip ist fuer die Unterstuetzung der Kaskadierung in spaeteren Versionen reserviert und sollte hier mit Null belegt werden. Der Ablauf ist analog zur Routine Copy2EEPROM, allerdings werden hier 3 Adressbyte uebertragen und die Pagesize ist 128.

void copy2LargeEEPROM(unsigned int base, unsigned char *buf, unsigned long page, unsigned int chip) { unsigned char adr_0, adr_1, adr_2; unsigned int i; unsigned long adr; unsigned long pagesize = 128; adr = page * pagesize; adr_0 = (adr & 0x0000ff); adr_1 = (adr & 0x00ff00) >> 8; adr_2 = (adr & 0xff0000) >> 16; STARTBIT(base);

139

if (chip) WriteAddressByte(base, 0xae);// 1 0 1 0 else // ^ ^ WriteAddressByte(base, 0xa6);//

x 1 1 0 [chip] [r/w]

WriteAddressByte(base, adr_2); WriteAddressByte(base, adr_1); WriteAddressByte(base, adr_0); for (i = 0; i < pagesize; i++) WriteDataByte(base, buf[i]); STOPBIT(base); delay(20); } /****************************** Loeschen ***************************** * * Das gesamte EEPROM wird mit ‚0‘ gefuellt * **********************************************************************/ void clear_eeprom(struct Parameter *parameter) { unsigned long count; char buf[BLOCKSIZE]; unsigned long fsize, page, pagesize, l, l0, type; memset(buf, 0, BLOCKSIZE); type = parameter->eeprom_type; fsize = type * 1024 / 8; if (parameter->eeprom_type < 512) pagesize = 64; else pagesize = 128; fprintf(stderr, „Loesche EEPROM\n“); for (count = 0, page = 0; count < fsize; count += pagesize, page++) { if (parameter->eeprom_type < 512) copy2EEPROM(parameter->base, buf, page, 0); else copy2LargeEEPROM(parameter->base, buf, page, 0); fprintf(stderr, „%3u %%>\r“, (unsigned int) ((page * pagesize * 100.0) / fsize)); fflush(stderr); } fprintf(stderr, „100 %%>\n“); } /*********************** Laderoutine fuer Binaerformat ****************** * * Die Daten werden unveraendert aus einer Datei in da EEPROM geladen * ************************************************************************/ void UploadBIN(FILE * f, struct Parameter *parameter) { unsigned long page, pagesize, Size; unsigned char buf[BLOCKSIZE];

140

Ladeprogramm für EEPROM

if (parameter->eeprom_type < 512) pagesize = 64; else pagesize = 128; Size = fsize(f); page = 0; while (!feof(f)) { fread(buf, 1, pagesize, f); if (parameter->eeprom_type < 512) copy2EEPROM(parameter->base, buf, page, 0); else copy2LargeEEPROM(parameter->base, buf, page, 0); page++; fprintf(stderr, „%3u %%>\r“, (unsigned int) ((page * pagesize * 100.0) / Size)); fflush(stderr); } fprintf(stderr, „\n“); }

/******************** Laderoutine fuer das MCS Format ****************** * * Die Daten aus dem MCS File werden dekodiert und in das EEPROM geladen * ***********************************************************************/

void UploadMCS(FILE * f, struct Parameter *parameter) { unsigned long SourceOffset; unsigned long page, pagesize, Size; struct FPGAData *data; if (parameter->eeprom_type < 512) pagesize = 64; else pagesize = 128; Size = MCSDataSize(f); SourceOffset = 0; page = 0; while ((data = mcs2bin(f, SourceOffset, pagesize))) { if (parameter->eeprom_type < 512) copy2EEPROM(parameter->base, data->block, page, 0); else copy2LargeEEPROM(parameter->base, data->block, page, 0); SourceOffset = data->SourceOffset; free(data); page++; fprintf(stderr, „%3u %%>\r“, (unsigned int) ((page * pagesize * 100.0) / Size)); fflush(stderr); } fprintf(stderr, „\n“); }

141

/********************* Laderoutine fuer das RBT fileformat ************** * * Die Daten aus dem RBT File werden dekodiert und in das EEPROM geladen * ************************************************************************/ void UploadRBT(FILE * f, struct Parameter *parameter) { unsigned long SourceOffset; unsigned long page, pagesize, Size; struct FPGAData *data; if (parameter->eeprom_type < 512) pagesize = 64; else pagesize = 128; Size = RBTDataSize(f); SourceOffset = 0; page = 0; while ((data = rbt2bin(f, SourceOffset, pagesize))) { if (parameter->eeprom_type < 512) copy2EEPROM(parameter->base, data->block, page, 0); else copy2LargeEEPROM(parameter->base, data->block, page, 0); SourceOffset = data->SourceOffset; free(data); page++; fprintf(stderr, „%3u %%>\r“, (unsigned int) ((page * pagesize * 100.0) / Size)); fflush(stderr); } fprintf(stderr, „\n“); }

/***************************** Hauptprogramm ****************************** * * ***************************************************************************/ int main(int argc, char **argv) { FILE *SourceFile;/* File, aus dem Daten ausgelesen werden */ FILE *LogFile;/* File, in das geschrieben wird (.log) */ struct Parameter *parameter; time_t t; char *date; struct FPGAData *Data; t = time(&t); date = ctime(&t); // Kommandozeilenparameter lesen parameter = (struct Parameter *) GetArgs(argc, argv); if (parameter == NULL) { fprintf(stderr, „Error bei Parameteruebergabe\n“);

142

Ladeprogramm für EEPROM

exit(-1); } if (strcmp(parameter->LogFileName, „“)) LogFile = OpenOrDie(parameter->LogFileName, „ab“); else LogFile = NULL;// kein Logfile if (!parameter->clear) SourceFile = OpenOrDie(parameter->FileName, „rb“); if (parameter->verbose) { fprintf(stderr, „%s\n“, VERSION); fprintf(stderr, „Filename: %s\n“, parameter->FileName); fprintf(stderr, „Logfile: %s\n“, parameter->LogFileName); fprintf(stderr, „Basisadresse: %X\n“, parameter->base); fprintf(stderr, „EEPROM Type: %d kbit\n“, parameter->eeprom_type); switch (parameter->fileformat) { case BIN: fprintf(stderr, „Fileformat: binaer\n“); break; case RBT: fprintf(stderr, „Fileformat: text (rbt)\n“); break; case MCS: fprintf(stderr, „Fileformat: text (mcs)\n“); break; } fprintf(stderr, „\n“); } if (LogFile) { fprintf(LogFile, „\n“); fprintf(LogFile, „*************** *********************\n“); fprintf(LogFile, „\n“); fprintf(LogFile, „Datum: %s\n“, date); fprintf(LogFile, „Filename: %s\n“, parameter->FileName); fprintf(LogFile, „\n“); } // Vor Beginn der Programmierung sind folgende Signale zu setzen: // // PROG = 0 .. FPGA abschalten, um Stoerungen zu vermeiden // SER_ENA = 1 .. ermoeglicht Schreiben des EEPROM // SLAVEOUT = 1 .. Trennt FPGA vom EEPROM // // (siehe auch README.hardware) im Unterverzeichnis doc // // Das Setzen der Signale erfolgt wieder durch Schreibzugriffe // auf eine Adresse im PC: // // Offset 0x806: BIT 0: PROG // BIT 1: SLAVEOUT // BIT 2: SER_ENABLE // // pokeb(parameter->base, 0x806, 0x6);// prog=0, ser_ena=1, slaveout=1 delay(100); pokeb(parameter->base, 0x806, 0x2);// prog=0, ser_ena=0, slaveout=1 delay(1200); // diese relativ lange Pause ist notwendig, ansonsten // wird das EEPROM unvollstaendig beschrieben. // (fehlen der ersten 2 .. 3 bloecke)

143

STOPBIT(parameter->base); delay(100); // EEPROM loeschen bzw. programmieren if (parameter->clear) clear_eeprom(parameter); else switch (parameter->fileformat) { case RBT: UploadRBT(SourceFile, parameter); break; case BIN: UploadBIN(SourceFile, parameter); break; case MCS: UploadMCS(SourceFile, parameter); break; default: fprintf(stderr, „Zur Zeit werden nur RBT, \ MCS und BIN Formate unterstuetzt\n“); break; } delay(100);

// war 1000 in ver 0.96

pokeb(parameter->base, 0x806, 0x4);// prog=0, slaveout=0, ser_ena=1 delay(100);

// war 1000 in 0.96

pokeb(parameter->base, 0x806, 0x5);// prog=1, slaveout=0, ser_ena=1 delay(100);

// war 1000 in 0.96

if (LogFile) { fprintf(LogFile, „\n“); fprintf(LogFile, „*************** *******************\n“); } if (SourceFile) fclose(SourceFile); if (LogFile) fclose(LogFile); return 0; } /*------------------------------------------------------------------------Programm eload ================

Datei: parse.c

(Auswertung der Kommandozeilenparameter)

-------------------------------------------------------------------------*/

#include #include #include #include

144

Ladeprogramm für EEPROM

#include „eload.h“ void help(char *); void version(void); int AskUser(char *); char *GetExtension(char *); void help(char *s) { fprintf(stderr, „\n“); fprintf(stderr, „Syntax: eload [Optionen] \n“); fprintf(stderr, „ -V Version anzeigen \n“); fprintf(stderr, „ -v ausfuehrliche Ausgabe\n“); fprintf(stderr, „ -x: Groesse des eeproms \n“); fprintf(stderr, „ -d loesche eeprom \n“); fprintf(stderr, „ -b: Basisadresse (hex) \n“); fprintf(stderr, „ -l: Pfade des Logfiles \n“); fprintf(stderr, „ -h , --help diese Hilfe \n“); fprintf(stderr, „ -t: Format der zu ladenden Datei \n“); fprintf(stderr, „ \n“); fprintf(stderr, „ Zulaessige Argumente: \n“); fprintf(stderr, „ eeprom_typ: 64, 128, 256, 512, 1024 [kbit] \n“); fprintf(stderr, „ Dateiformate: RBT, MCS, BIN \n“); fprintf(stderr, „ \n“); fprintf(stderr, „ Defaultwerte: \n“); fprintf(stderr, „ eeprom_typ: 1024 Basisadresse: D000 Logfile: keins \n“); fprintf(stderr, „ \n“); fprintf(stderr, „ Beispiel: \n“); fprintf(stderr, „ eload -b:D800 -t:rbt -tMeinLogFile -x:256 Datei.xzy \n“); fprintf(stderr, „ - Datei.xyz ist im RBT Format, geladen wird in ein 256 kbit\ grosses\n“); fprintf(stderr, „ EEPROM, die Basisadresse ist D800, das Logfile heisst \ \“MeinLogFile\“. \n“); exit(-1); } void version() { printf(„%s\n“, VERSION); exit(0); } // Konvertierung in Integer int GetVal(char *arg, int i, int b) { char *s; int offset; /* Unter DOS werden Leerzeichen in der Kommandozeile zu 0x00 Byte (?) */ #ifdef DOS #define SPACE 0 #endif #ifdef UNIX #define SPACE 0 #endif

145

for (offset = 0; ((arg[2 + offset] == ‚:‘) || (arg[2 + offset] == SPACE)); offset++); s = arg + i + offset; if (s) return strtol(s, NULL, b); else return -1; } // Zeichenketten (z.B bei Filenamen) char *GetStrVal(char *arg, int i) { char *s, *r; int offset; /* Unter DOS werden Leerzeichen in der Kommandozeile zu 0x00 Byte (?) */ #ifdef DOS #define SPACE 0 #endif #ifdef UNIX #define SPACE 0 #endif for (offset = 0; ((arg[2 + offset] == ‚:‘) || (arg[2 + offset] == SPACE)); offset++); s = arg + i + offset; r = (char *) malloc(strlen(s) + 1); strcpy(r, s); return r; }

/* entspricht der UNIX routine ‚basename‘ */ /* Bsp: basename(„/data/src/foo.c“) ergibt „foo.c“ char *basename(char *path) { #ifdef DOS char d[] = „\\“; #endif #ifdef UNIX char d[] = „/“; #endif char *s, *r; for (s = path; s;) if (strstr(s, d)) s = strstr(s, d) + 1; else { r = (char *) malloc(strlen(s) + 1); strcpy(r, s); return r; }

*/

146

Ladeprogramm für EEPROM

return NULL; }

int FoundParameter(char *s, char *p) { char *x; x = strstr(s, p); if (x == NULL) return 0; else if (x == s) return 1; else if (*(x - 1) == ‚ ‚) return 1; else return 0; } struct Parameter *GetArgs(int argc, char **argv) { char FileName[256]; char *ExtensionStr; int i; int base = -1; int eeprom_type = -1; int cascade = 0; int verbose = 0; int clear = 0; int Extension = BIN; char *LogFileName = NULL; char *FileType = NULL; struct Parameter *parameter; strcpy(FileName, „“); if (argc < 2) { help(argv[0]); exit(-1); } else { for (i = 1; i < argc; i++) { if ((FoundParameter(argv[i], „-h“)) || (FoundParameter(argv[i], „--help“))) help(argv[0]); else if (FoundParameter(argv[i], „-V“)) version(); else if (FoundParameter(argv[i], „-b“)) base = GetVal(argv[i], 2, 16); else if (FoundParameter(argv[i], „-x“)) eeprom_type = GetVal(argv[i], 2, 10); else if (FoundParameter(argv[i], „-v“)) verbose = 1; else if (FoundParameter(argv[i], „-c“)) cascade = 1; else if (FoundParameter(argv[i], „-d“)) clear = 1; else if (FoundParameter(argv[i], „-l“)) LogFileName = GetStrVal(argv[i], 2); else if (FoundParameter(argv[i], „-t“)) FileType = GetStrVal(argv[i], 2); else strcpy(FileName, argv[i]); }

147

} if (base == -1) base = DEFAULT_BASE; if (eeprom_type == -1) eeprom_type = DEFAULT_EEPROM_TYPE; if ((!strcmp(FileName, „“)) && (clear == 0)) help(argv[0]); if (LogFileName == NULL) { LogFileName = (char *) malloc(sizeof(DEFAULT_LOGFILENAME)); strcpy(LogFileName, DEFAULT_LOGFILENAME); } // Der Typ der zu ladenden Datei wird wie folgt bestimmt: // a) zuerst wird der Parameter -t ausgewertet. -t:RBT erzwingt z.B. // RBT Format // b) wurde kein Parameter angegeben, wird versucht, den Typ aus der // Extension zu ermitteln // c) Schlaegt a) bzw. b) fehl, so wird der Nutzer interaktiv // aufgefordert, den Typ anzugeben // if (!clear) { if (FileType) { for (i = 0; i < strlen(FileType); i++) FileType[i] = tolower(FileType[i]); if (strstr(FileType, „rbt“)) Extension = RBT; else if (strstr(FileType, „mcs“)) Extension = MCS; else if (strstr(FileType, „bin“)) Extension = BIN; else Extension = AskUser(FileType); } else { // Extension des zu ladenden Files ExtensionStr = GetExtension(FileName); if (!strcmp(ExtensionStr, „.rbt“)) Extension = RBT; else if (!strcmp(ExtensionStr, „.mcs“)) Extension = MCS; else if (!strcmp(ExtensionStr, „.bin“)) Extension = BIN; else Extension = AskUser(ExtensionStr); } if (Extension == UNKNOWN) exit(-1); } parameter = (struct Parameter *) malloc(sizeof(struct Parameter)); strcpy(parameter->FileName, FileName); strcpy(parameter->LogFileName, LogFileName); parameter->verbose = verbose; parameter->base = base; parameter->eeprom_type = eeprom_type; parameter->cascade = cascade; parameter->clear = clear; parameter->fileformat = Extension; return parameter; }

148

Ladeprogramm für EEPROM

/*------------------------------------------------------------------------Programm eload ================

Datei: convert.c

(Konvertierung)

-------------------------------------------------------------------------*/ #include #include #include #include

„eload.h“

// Diese Funktion ermittelt, wieviele Bytes aus einer RBT Datei // decodiert werden koennen unsigned long RBTDataSize(FILE * f) { char zeile[2048]; unsigned long i, count = 0; unsigned long PointerPosition; unsigned long Size; // // // // //

Wenn das erste Zeichen einer Zeile im RBT File eine 1 oder 0 ist, so wird sie als Datenzeile angenommen, ansonsten als Kommentar Wird innerhalb einer Zeile eine Zeichen entdeckt, welches keine 1 oder 0 ist, so wird an dieser Stelle das Ende der Zeile angenommen

PointerPosition = ftell(f); fseek(f, 0, SEEK_SET); while (fgets(zeile, 2048, f)) { if ((strstr(zeile, „1“) == zeile) || (strstr(zeile, „0“) == zeile)) for (i = 0; zeile[i] == ‚0‘ || zeile[i] == ‚1‘; i++, count++); } fseek(f, PointerPosition, SEEK_SET); Size = count / 8; return Size; // jede ‚1‘ bzw. ‚0‘ innerhalb einer Datenzeile ist ein Bit }

// Diese Funktion errechnet, wieviele Byte aus einer MCS Datei // dekodiert werden koennen unsigned long MCSDataSize(FILE * f) { unsigned char zeile[2048]; unsigned long PointerPosition; unsigned long Count = 0; PointerPosition = ftell(f); fseek(f, 0, SEEK_SET); fgets(zeile, 2048, f);// erste zeile ignorieren // Ein Datenbyte wird durch 2 ASCII Zeichen hexadezimal dargestellt. // Jede Zeile beginnt mit 9 ASCII Zeichen fuer die Adresse und endet mit

149

// // // //

2 ASCII Zeichen fuer das Checksummenbyte sowie einem Zeilenumbruch Die Anzahl der Datenbyte pro Zeile ergibt sich also also der Zeilenlaenge abzueglich 12 Zeichen ( 9 Adress, 2 checksumme, 1 Zeilenumbruch), dividiert durch 2

while (fgets(zeile, 2048, f)) if (strlen(zeile) > 12) Count += ((strlen(zeile) - 12) / 2); fseek(f, PointerPosition, SEEK_SET); //fprintf(stderr, „%u Byte werden geladen\n“, (unsigned int) Count); return Count; }

// Konvertiert MCS Dateien ins Binaerformat struct FPGAData *mcs2bin(FILE * f, unsigned long SourceOffset, unsigned long pagesize) { unsigned long offset = 0; unsigned long i, start; unsigned char buf[BLOCKSIZE]; unsigned char zeile[2048]; struct FPGAData *data; unsigned char HexByteStr[4]; char **error; memset(buf, 0, sizeof(buf)); fseek(f, SourceOffset, SEEK_SET);// Ab dort weiterlesen, wo man bei // Aufruf vorher aufgeho*rt hat if (SourceOffset == 0) fgets(zeile, 2048, f);

// erste zeile ignorieren

offset = 0; while ((offset < pagesize) && fgets(zeile, 2048, f)) { // Adressfeld ignorieren if (zeile[0] == ‚:‘) start = 9; else start = 0; for (i = start; (offset < pagesize) && (i < strlen(zeile) - 4); i += 2) { // 2 Ascii-Zeichen stellen ein Byte als Headezimalzahl dar strcpy(HexByteStr, „“); strncat(HexByteStr, zeile + i, 2); buf[offset] = (unsigned char) strtol(HexByteStr, NULL, 16); offset++; } }

// 64 bzw. 128 Byte in eine Struktur FPGAData schreiben. // Es werden immer ganze Bloecke zu 64/128 Byte benutzt. // Eventuell ist der letzte Block mit Nullen aufgefuellt. if (offset) { data = (struct FPGAData *) malloc(sizeof(struct FPGAData)); if (data == NULL) { perror(„Fehler bei Speicheranforderung“);

150

Ladeprogramm für EEPROM

exit(-1); } memcpy(data->block, buf, pagesize); // FilePointer im Sourcefile data->SourceOffset = (ftell(f) - strlen(zeile)) + i; // ist eine zeile ausgelesen, soll beim folgenden aufruf von mcs2bin // mit einer neuen zeile begonnen werden. // Die verbliebenen Bytes werden also uebersprungen. if (i == (strlen(zeile) - 3)) data->SourceOffset += 3; return data; } else return NULL;// Sourcefile ausgelesen

}

// Konvertiert RBT Dateien ins Binaerformat. // Siehe auch eload.h fuer weitere Hinweise struct FPGAData *rbt2bin(FILE * f, unsigned long SourceOffset, unsigned long pagesize) { char c, zeile[2048]; char maske[] = {128, 64, 32, 16, 8, 4, 2, 1}; unsigned long int i; unsigned char byte; unsigned long int bit_no; unsigned int offset = 0; unsigned char buf[BLOCKSIZE]; struct FPGAData *data; byte = 0; bit_no = 0; memset(buf, 0, sizeof(buf)); fseek(f, SourceOffset, SEEK_SET);// Ab dort weiterlesen, wo man bei // Aufruf vorher aufgeho*rt hat offset = 0; while ((offset < pagesize) && fgets(zeile, 2048, f)) { if ((strstr(zeile, „1“) == zeile) || (strstr(zeile, „0“) == zeile)) { for (i = 0; ((offset < pagesize) && ((zeile[i] == ‚0‘) || (zeile[i] == ‚1‘))); i++) { if (i > 2047) { fprintf(stderr, „ERROR! Zeile im RBT-File > 2048 Zeichen !!\n“); exit(-1); } if (zeile[i] == ‚1‘) { byte |= maske[7 - (bit_no % 8)]; bit_no++; } if (zeile[i] == ‚0‘) { byte &= ~maske[7 - (bit_no % 8)]; bit_no++; }

151

if ((bit_no % 8) == 0) { buf[offset] = byte; offset++; } } } } // 64 bzw. 128 Byte in eine Struktur FPGAData schreiben. // Es werden immer ganze Bloecke zu 64/128 Byte benutzt. // Eventuell ist der letzte Block mit Nullen aufgefuellt. if (offset) { data = (struct FPGAData *) malloc(sizeof(struct FPGAData)); if (data == NULL) { perror(„Fehler bei Speicheranforderung“); exit(-1); } memcpy(data->block, buf, pagesize); // FilePointer im Sourcefile data->SourceOffset = (ftell(f) - strlen(zeile)) + i; return data; } else return NULL;// Sourcefile ausgelesen } /*------------------------------------------------------------------------Programm eload ================

Datei: ext.c

(Dateityperkennung)

-------------------------------------------------------------------------*/

#include #include #include #include #include

„eload.h“

int AskUser(char *e) { unsigned char c; fprintf(stderr, „Das Dateiformat kann aus der Extension (‚%s‘) nicht \ bestimmt werden.\n“, e); fprintf(stderr, „Bitte geben Sie das Dateiformat an:\n“);

c = 0; while ((c != ‚b‘) && (c != ‚B‘) && (c != ‚m‘) && (c != ‚M‘) && (c != ‚r‘) && (c != ‚R‘) && (c != ‚a‘) && (c != ‚A‘)) { if (c != 0x0a) fprintf(stderr, „[B]in / [R]BT / [M]CS / [A]bbruch „); fflush(stderr); c = getc(stdin); } if ((c == ‚b‘) || (c == ‚B‘))

152

Ladeprogramm für EEPROM

return BIN; else if ((c == ‚m‘) || (c == ‚M‘)) return MCS; else if ((c == ‚r‘) || (c == ‚R‘)) return RBT; else return UNKNOWN; } // Liefert die Extension einer Dateinamen char *GetExtension(char *dateiname) { char *s0, *a, *s; char *extension; int i; a = dateiname; s0 = NULL; do { s = strstr(a, „.“); if (s) { s0 = s; a = s + 1; } } while (s); extension = malloc(strlen(s0) + 1); strcpy(extension, s0); for (i = 0; i < strlen(s); i++) extension[i] = _tolower(extension[i]); return extension; }

/*------------------------------------------------------------------------Programm eload ================

Datei: eload.h -------------------------------------------------------------------------*/

// #define DEBUG // #define UNIX #define DOS #define #define #define #define

VERSION „eload 1.0“ DEFAULT_BASE 0xd000 DEFAULT_EEPROM_TYPE 1024 DEFAULT_LOGFILENAME „“// default: Kein Logfile

// unterstuetzte Dateiformate #define UNKNOWN 0// #define BIN 1// Binaerdump #define RBT 2// RBT Format

153

#define MCS

3// MCS Format

// Defines aus eload.c #define #define #define #define #define

SER_REG_ADR SER_ENABLE_ADR DATA_BIT CLOCK_BIT BASE_ADR

0x800 0x806 0 1 DEFAULT_BASE

struct Parameter { char FileName[256]; char LogFileName[256]; int eeprom_type;// 64, 128, 256, 512, 1024 [kbit] int base;// Basisadresse int verbose; int cascade;// fuer spaetere Versionen int clear;// Fuellen des EEPROM mit ‚0‘ int fileformat;// BIN, RBT oder MCS-Format };

// // // // // // // // // // // // // //

FPGA Konfigurations Daten. Die Funktion RBT2BIN liefert bei einem Aufruf jeweils einen Block von 64 bzw. 128 Byte zurueck. Die Funktion wird so oft hintereinander aufgerufen, bis die RBT/MCS Datei vollstaendig ausgelesen ist. In der Struktur FPGAData werden die Daten sowie der aktuelle Offset in die Sourcedatei (rbt/mcs) zurueckgeliefert. Bsp: Nach dem ersten aufruf stehen in buf 128 Byte ‚decodierte‘ Daten, der offset in die Sourcedatei koennte z.B 128*8 Byte betragen (da in RBT jedes Bit durch ein Character dargestellt wird.) Durch Headerkommentare und Zeilenumbrueche kann der Offset auch entsprechend groesser ausfallen.

#define BLOCKSIZE 128 struct FPGAData { // das ist die groesste pagesize fuer die eeproms char block[BLOCKSIZE]; // der offset in die Quelldatei (rbt oder mcs) unsigned long SourceOffset; unsigned long Zeilennummer;// fuer mcs files };

/*------------------------------------------------------------------------Programm eload ================

154

Ladeprogramm für EEPROM

Datei: make.bat (Makescript fuer Borland C Compiler) -------------------------------------------------------------------------*/

@echo off bcc bcc bcc bcc bcc bcc

-

c c c c c c

ext.c Convert.c eload.c parse.c rbt2bin.c mcs2bin.c

bcc eload.obj ext.obj Convert.obj parse.obj bcc rbt2bin.obj Convert.obj bcc mcs2bin.obj Convert.obj copy eload.exe.. \ bin del eload.exe copy rbt2bin.exe.. \ bin del rbt2bin.exe copy mcs2bin.exe.. \ bin del mcs2bin.exe del del del del del del

eload.obj parse.obj ext.obj Convert.obj rbt2bin.obj mcs2bin.obj

155

Anhang E

Testprogramme Grundtest 1

/*---------------------------------------------------------------Grundtest 1 =========== Datei: rvect403.s

(resetvektor)

----------------------------------------------------------------*/

.globlrvector .text .space0xFC rvector: b

rvector

/*---------------------------------------------------------------------Programm Grundtest 1 ===================

File: corep1.lnk (Linkfile) ----------------------------------------------------------------------*/ MEMORY {

156

Testprogramme

reset: org = 0xffffff00, len = 0x100 } SECTIONS { .init (TEXT) : { rvect403.o(.text) } > reset }

157

Grundtest 2 /*------------------------------------------------------------------------Programm Grundtest 2 ==================== Datei: rvect403.s

(resetvektor)

--------------------------------------------------------------------------*/ .globlrvector .text .space0xFC rvector: b

_reset

/*------------------------------------------------------------------------

Programm Grundtest 2 ==================== Datei: init.s --------------------------------------------------------------------------*/

.globl_reset .globl main

#include bcr.inc .text

msr_mon:

.equ0x00000000

#for GCX: #INSTRUCTION_CACHE_LINES: #DATA_CACHE_LINES: # for GA: # INSTRUCTION_CACHE_LINES: DATA_CACHE_LINES:

.equ .equ

.equ .equ

512 256

# GCX: 512 Lines, GA: 64 Lines # GCX: 256 Lines, GA: 32 Lines

64 32

############################################################################ _reset:

# Invalidate Instruction Cache #Invalidate Line 0 .. 512 (for GCX, GA CPUs only have 64 Lines ! ) li r1,0 # Start: Line 0 li r2, INSTRUCTION_CACHE_LINES

158

Testprogramme

mtctrr2 icloop: .long(31

[PDF] Diplomarbeit. Implementierung und Test einer Emulationsplattform für die Hardware- Softwarepartitionierung eingebetteter Systeme. - Free Download PDF (2024)
Top Articles
Latest Posts
Article information

Author: Trent Wehner

Last Updated:

Views: 6017

Rating: 4.6 / 5 (76 voted)

Reviews: 91% of readers found this page helpful

Author information

Name: Trent Wehner

Birthday: 1993-03-14

Address: 872 Kevin Squares, New Codyville, AK 01785-0416

Phone: +18698800304764

Job: Senior Farming Developer

Hobby: Paintball, Calligraphy, Hunting, Flying disc, Lapidary, Rafting, Inline skating

Introduction: My name is Trent Wehner, I am a talented, brainy, zealous, light, funny, gleaming, attractive person who loves writing and wants to share my knowledge and understanding with you.