Home Milan Hardware Milan PCI Hardware 1 Milan SCSI Hardware
 

5.11 Milan PCI Hardware 2


Sonder-Edition PCI-BIOS MILAN




Sonder-Edition PCI-BIOS Stand: 09. 07.1998

Von:
Markus Fichtenbauer
Torsten Lang
Michael Schwingen

Der PCI-Bus (Part 1)

Heute ist es soweit: Mit dem Milan und dem Hades stehen den ATARI-Anwendern inzwischen zwei Rechner zur Verfügung, die einen PCI-Bus besitzen. Schon bald soll ein Falcon-PCI-Adapter erscheinen, und eines Tages wird auch der französische Phenix mit PCI-Bus zu erhalten sein. Alles deutet also darauf hin, daß PCI-Karten nun auch für das TOS angepasst werden. Erfreulicherweise ist die Entwicklung in diesem Bereich soweit vorangeschritten, daß ein gemeinsames PCI-Bios entwickelt wurde, das ermöglicht, daß einmal angepasste Treiber auf allen o.g. Systemen laufen werden. In unserem mehrteiligen Kurs wollen wir Sie über die Funktionsweise des PCI-Busses und anschliessend die Programmierung des TOS PCI-Bios informieren.

Allgemeines:

Der Peripheral Components Interconnect (PCI) Bus wurde ursprünglich für IBM-kompatible PCs entwickelt, da dringend die Notwendigkeit eines schnellen, prozessorunabhängigen Bussystems bestand. Seither hat sich dieser Bus auch ür andere Rechnerwelten etabliert (wie z.B. die aktuellen Macintosh-Modelle auf Basis des PowerPC, diverse Workstations mit DECs Alpha-Prozessor oder aber auch die aktuelle UltraSparc von SUN). Auch die Entwickler des Hades und des Milan haben an diese wirklich universelle Schnittstelle gedacht. Diese Entwicklung führte dahin, daß man nun tatsächlich dieselben Erweiterungskarten auf unterschiedlichen Rechnerplattformen nutzen kann - sofern jeweils Treiber ür das betreffende System verügbar sind. Daher ist es längst an der Zeit, sich einmal näher mit dem PCI-Bus zu beschäftigen. Diese erste Folge soll einen überblick der Möglichkeiten des PCI-Bus schaffen, die zweite Folge beschreibt dann die einzelnen Register für die Initialisierung und Konfiguration von PCI-Karten, und die abschließende dritte Folge befaßt sich dann mit der Portierung und Implementierung des vom PC her bekannten PCI-BIOS für ATARI-kompatible Rechner am Beispiel von Hades und Milan. Der PCI-Bus ist für 32 Bit breite Daten bei maximal 33 MHz spezifiziert und ist optional auf 64 Bit und/oder 66 MHz erweiterbar. Der Takt darf aber im Prinzip einen beliebigen Wert zwischen 0 und 33 MHz annehmen. Bei 33 MHz und 32 Bit Datenbreite sind theoretische Übertragungsraten von bis zu 132 MB/s möglich, und in der Praxis sind tatsächlich Werte nahe diesem theoretischen Maximum erreichbar. Nach der elektrischen Spezifikation künnen an einem PCI-Bus bis zu vier Slots neben dem Hostsystem untergebracht sein. über eine PCI-Bridge läßt sich dann noch ein weiterer Bus anhängen. Falls dies wieder ein PCI-Bus ist, kann man das Spiel auch mehrfach wiederholen. Jedes PCI-Gerät kann mehrere unabhängige Funktionen beinhalten (multifunction devices), die sich auch unabhängig über den Bus ansprechen lassen. Die meisten derzeit erhältlichen Geräte sind allerdings sogenannte 'single function devices'. Multi function devices verhalten sich prinzipiell wie mehrere single function devices, nur, daß eben die Configuration Bereiche der einzelnen Funktionen dieser Karten anders angesprochen werden. Bei den Multifunktionskarten kann man zwei von Grund auf verschiedene Versionen unterscheiden:

1.
Nur ein einziger Bus - die Konfigurationsbereiche solcher PCI-Karten liegen jeweils 256 Bytes auseinander (d.h. die Adressleitungen spiegeln einfach Register (Bit 0-7) und Funktionsnummer (Bit 8-15) der Karte wieder.
 
2.
PCI2PCI-Bridge mit single function devices oder multi function devices vom Typ 1 am zweiten Bus - so etwas kommt z.B. bei Kombikarten wie Ethernet + SCSI vor. Da die Busnummer hier über die Adressleitungen A16-23 ausgewählt werden würde, lassen sich solche Karten beim Hades wahrscheinlich nicht oder nur eingeschränkt verwenden (weil die Konfigurationsbereiche ja auf wenige KB beschränkt sind).
 



Die PCI-Hardware:

Für die grundlegenden Funktionen des PCI-Bus werden insgesamt 47 Pins für Slave-Adapter sowie 49 Pins für Master-Adapter benötigt. Dazu kommen die Anschlüsse der Versorgungsspannung. Auf eine Aufzählung aller Signale finden Sie ganz am Anfang dieses Artikels, eine genaue Beschreibung aller Signale erfolgt hier noch nicht, da diese den Artikel sichtlich sprengen würden. Deshalb sollen nur die wichtigsten, in den Abbildungen verwendeten Signale näher erläutert werden:

AD 80..31
- Address/Data

 
Adressen und Daten werden gemultiplext übertragen. Die einzelnen Bytes des 32-Bit-Wortes werden über die Leitungen /C/BE[0..3] freigegeben.
 
/C/BE 0..3
- Command/Byte Enable

 
Enthält in der Adressphase das Buskommando (verschiedene Lese- und Schreibarten) und in der Datenphase das Signal Byte Enable für die Datenleitungen.
 
IDSEL
- Initialization Device Select

 
Chip Select für Initialisierung und Konfiguration der PCI-Karte.für jeden PCI-Slot ist eine eigene Leitung vorhanden.
 
/REQ
- Request, wird nur für Busmastering verwendet

 
Busanforderung, für jeden Slot ist eine Leitung vorhanden.
 
/GNT
- Grant, wird nur für Busmastering verwendet.

 
Buszuteilung,für jeden Slot ist eine Leitung vorhanden.
 
/INTA
- Interrupt Request A

 
Anforderung eines Interrupts bei single function devices.
 
/INTB, C, D
- Interrupt Request B, C, D
 
Diese Interruptleitungen dürfen nur von Geräten mit mehreren Funktionseinheiten (multi function devices) verwendet werden. Die restlichen Signale dienen mehr oder weniger der Steuerung während den verschiedenen Adress- und Datenphasen und deren Synchronisation. Näheres findet man in der PCI Local Bus Specification und den PCI System Design Guides, einen guten überblick kann man sich aber auch mittels der Elrad-Hefte 3/97 und 4/97 verschaffen. Da wir hier nicht neue PCI-Karten entwickeln möchten, sondern aus dem PC-Bereich bereits vorhandene Karten auch dem Hades, Milan und anderen kompatiblen ATARI-Rechnern schmackhaft machen müchten, verzichten wir hier auf weitere Hardware-Details, diefür Treiber-Anpassungen nicht relevant sind. Die korrekte Verarbeitung der Signale sollte ja ohnehin von den in den Rechnern vorhandenen PCI-Bridges übernommen werden. Wer aber trotzdem (oder gerade deswegen) eigene Karten entwickeln müchte, sollte sich Datenblätter der PCI-Bridges von AMCC[4] und PLX[5] besorgen. Wer das PCI-Busprotokoll selbst implementieren möchte, benötigt daür relativ schnelle CPLDs oder FPGAs. Hersteller wie Xilinx[6] oder Lattice[7] bieten daür auch Beispiel-Sourcen an.
 



DMA am PCI-Bus:

Grundsätzlich kann jedes beliebige PCI-Gerät als Busmaster auftreten. Als Busmaster kann dieses PCI-Gerät dann selbständig Daten aus seinem eigenen Speicherbereich in den Systemspeicher des Host-Rechners übertragen. Diese Art der Hintergrundverarbeitung entlastet den Prozessor des Host-Rechners sehr stark. Deshalb sollten alle PCI-Geräte, die große Datenmengen zu transportieren haben, oder aber schnelle Transfers erfordern, als Busmaster auftreten künnen. Die DMA-Fähigkeit des PCI-Bus benutzen z.B. SCSI-Controller oder auch moderne Netzwerkkarten, um Daten schnell in den oder aus dem Hauptspeicher zu transportieren. Die Vorteile bei Verwendung eines busmasterfähigen PCI-Gerätes zeigen sich ganz besonders bei 'echten' Multitasking-Betriebssystemen, da dort die gesparte Rechenzeit anderen Prozessen zur Verfügung gestellt werden kann. Unter Single-Tasking unterliegt es dem Geschick des Programmierers, aus der Busmaster-Fähigkeit eines PCI-Gerätes Vorteile zu ziehen. Ein Zugriff, der nur auf das Ende einer übertragung wartet, ist zwar einfacher zu implementieren, verschenkt durch Leerlauf des Prozessors aber wieder die gewonnene Zeit. Das busmasterfähige PCI-Gerät fordert den Bus durch Setzen seiner /REQLeitung an. Ein Master darf /REQ allerdings nurfür seinen aktuellen Zugriff benutzen, nicht aber, um sich den Bus auf Dauer zu sichern. Der Bus Arbiter des Host-Rechners teilt diesem Gerät den Bus durch Setzen des entsprechenden /GNT-Signals zu. Nachdem dem PCI-Gerät der Bus zugeteilt wurde, kann dieses seinen DMA-Zugriff durchühren. Wenn das PCI-Gerät nicht innerhalb einer bestimmten Zeit den Bus wieder freigibt, kann der Host-Rechner durch das Wegnehmen der /GNT-Leitung den Bus auch wieder frei bekommen. Der Busmaster muß dann nämlich augenblicklich den Zugriff beenden.

Interrupts am PCI-Bus:

Auf dem PCI-Bus sind insgesamt vier "pegel"-getriggerte Interrupt-Leitungen vorhanden (INTA...INTD). Alle Geräte mit nur einer Funktion dürfen nur INTA verwenden, die anderen drei Leitungen sindfür den Betrieb sogenannter 'multi function devices' vorgesehen. In der PCI-Spezifikation selbst ist die weitere Verarbeitung der Interrupts nicht definiert und wird somit dem jeweiligen Boardhersteller überlassen. Die Interruptleitungen der einzelnen Slots künnen daher entweder als "durchgeschleifte" Busleitung (open drain) in jeder nur erdenklichen Kombination verbunden sein, oder aber sie werden von jedem Slot einzeln an den entsprechenden Interrupt Controller herangeührt. Nach einer Interruptanforderung ist dann der jeweilige Treiber dieses PCI-Gerätes daür verantwortlich, die Ursache der Unterbrechungsanforderung zu beheben. Im Gegensatz zum recht kranken Design des ISA-Busses ist es daher beim PCI-Bus müglich, daß sich mehrere Karten eine Interruptleitung teilen (shared interrupt) - Engpässe, wie man sie früher von den DOSen her kannte, werden damit vermieden. Allerdings müssen dann die eingesetzten Softwaretreiberfür die PCI-Karten auch Interrupts bearbeiten künnen, bei denen sich mehrere Geräte eine Interruptleitung teilen. Mehr zu diesem Thema gibt es dann bei der Vorstellung des PCI-BIOS.

Die Implementation des PCI-Bus im Milan:

Auch der Milan hat einen PCI-Bus mit einer Datenbreite von 32 Bit bei maximal 33 MHz. Die Anbindung an die CPU erfolgt über eine PCI-Bridge der Firma PLX. Es sind insgesamt 4 Interruptleitungen vorhanden, die zyklisch zwischen den Slots getauscht sind - dadurch bekommt bei single function devices jede Karte einen eigenen Interrupt, und erst bei multi function devices wird es nütig, Interrupts gemeinsam zu benutzen. Interrupts werden über die Intel PCI-ISA-Bridge und die darin vorhandenen Interrupt-Controller bearbeitet und als Autovektor-Interrupt an die CPU weitergemeldet. Die Busarbitrierung für die DMA-Zugriffe erfolgt in einem der Glue-Chips - alle Slots (sowie die Onboard-Peripherie) sind busmasterfähig. Es wird eine Arbitrierung mit rotierender Priorität verwendet, bei der die Karte, die gerade den Bus besitzt, die niedrigste Priorität hat, so daß eine Karte den Bus nicht komplett blockieren kann.  Wenn niemand den Bus anfordert, wird er bei dem Gerät, welches ihn zuletzt besaß, geparkt - das spart Zeit, wenn danach das gleiche Gerät den Bus wieder anfordert.

Erweiterungs-ROM auf PCI-Karten:

Die PCI-Spezifikation beschreibt auch einen Mechanismus, mit dem ein PCI-Gerät einen exekutierbaren ROM-Code für eine gerätespezifische Initialisierung usw. anbieten kann. Dieses ROM kann dabei mehrere verschiedene ROM-Images beinhalten, um die verschiedensten Rechner und Prozessorarchitekturen zu unterstützen. Der OPENBOOT Standard für PCI sieht Fortran-Programme im ROM vor, so daß der ROM-Code zwar langsam (weil interpretiert), aber daür prozessorunabhängig ist. Die andere Müglichkeit sind native ROM-Images (ür die jeweilige zu unterstützende Plattform), wovon die Hersteller aber keinen Gebrauch machen (wohl weil sich dann nur noch schwer begründen ließe, warum z.B. dieselbe Grafikkartefür einen PowerMac meist 100% teurer ist als für den Intel PC). Natürlich künnen OPENBOOT-Images und native Images gemeinsam in einem ROM untergebracht werden. Am ATARI werden die ROMs auf den Karten im Moment zwar noch nicht genutzt, aber dieser Umstand wird sich schon demnächst ändern.

Ein paar Worte zu Plug'n'Play:

Das Zauberwort 'Plug and Play' ist sehr eng mit dem PCI-Bus verbunden. Denn erst hier ist das leidige Thema der Ressourcenvergabe für die Hardware über ein geeignetes BIOS (PCI-BIOS) elegant lösbar. Im Idealfall sieht die Installation einer neuen PCI-Karte also folgendermaßen aus (im Gegensatz zum PC ist bei ATARI-kompatiblen ein nochmaliges (oder gar mehrmaliges) Booten während der Installation NICHT notwendig ;-)

* Karte in einen freien Slot reinstecken

* Rechner starten, das PCI-BIOS konfiguriert die Karte

* Softwaretreiber für die PCI-Karte starten und fertig

Der PCI-Bus ist eigentlich selbstkonfigurierend definiert. Dafür beinhaltet jedes PCI-Gerät einen Satz von Registern, die während des Boot-Vorgangs ausgelesen werden künnen. Enthalten sind hier neben Informationen über das Gerät selbst (Hersteller, Gerät, Typenklasse, Müglichkeiten, Waitstates, Interrupt, ...) auch die benütigten Speicherbereiche. Damit ist es dem BIOS müglich, die Karte korrekt ins System einzubinden, ohne daß Ressourcenkonflikte auftreten oder sich der Benutzer mit DIPSchaltern, Jumpern etc. herumschlagen muß. Am Macintosh-Sektor (NuBus) und am Amiga-Sektor (Zorro II/III-Bus) ist z.B. das Problem der Autokonfigurierung übrigens bereits seit der Existenz der jeweiligen Bussysteme (also seit über 10 Jahren) gelüst! Der PCI-Standard sieht mittlerweile auch optional die "Hot-Plug-Fähigkeit" vor, so daß PCI-Karten sogar im Betrieb gewechselt werden künnen. Das darf aber in keinem Fall am Hades oder Milan gemacht werden, da dies zur Zerstürung des Mainboards oder der Karte ühren kann (ür dieses Feature sind spezielle Voraussetzungen erforderlich).


Sonder-Edition PCI-BIOS, Part 2 Stand: 09. 07. 1998

Von:
Markus Fichtenbauer



Der PCI-Bus (Teil 2)

Wie bereits im ersten Teil dieser Serie erwähnt wurde, hat jedes PCI-Gerät einen eigenen Registersatz. Ein Zugriff auf diesen Konfigurationsbereich erfolgt immer durch paralleles Setzen der IDSEL-Leitung des ensprechenden Slots und dem Anlegen der Registeradresse und des Buskommandos Configuration Read oder Configuration Write auf den Leitungen /C/BE[0..3]. Die IDSEL-Leitung ist dabei für jeden Slot separat vorhanden und wird vom Motherboard auskodiert. Nur mit dieser Art der Steuerung ist ein Zugriff auf die einzelnen Karten direkt nach dem Reset möglich, da zu diesem Zeitpunkt noch keine Adreßräume zugeteilt wurden. Der Zugriff auf diese Register wird auf dem PC vom PCI-BIOS über die Befehle Configuration Read / Configuration Write bereitgestellt. Und in der nächsten Folge möchte ich dann sämtliche Möglichkeiten eines ATARI PCI-BIOS vorstellen

Der PCI Configuration Bereich:

Jede PCI-Karte enthält einen Satz von definierten Registern, die Informationen über die Karte bereitstellen und die die automatische Vergabe von Ressourcen überhaupt erst ermöglichen. Der Registerbereich ist 256 Bytes lang und teilt sich in einen von der PCI-SIG (PCI - Special Interest Group) vordefinierten und in einen kartenspezifischen Bereich. Dabei müssen in den jeweiligen PCI-Geräten von den beiden Bereichen nur die notwendigen Register implementiert werden Das Speicherlayout des 64 Byte langen, vordefinierten Bereichs selbst muß aber von jeder PCI-Karte eingehalten werden und beinhaltet die Identifikationsdaten der Karte und die Möglichkeiten zur generellen Kontrolle über die Karte. Die folgenden 192 Bytes sind nicht durch die PCI-Spezifikation vorgegeben, sondern können mit herstellerspezifischen Informationen gefüllt und danach vom Softwaretreiber ausgewertet werden.

Die Register werden während der Initialisierung des PCI-Busses ausgewertet und müssen daher schon direkt nach dem Reset gültige Werte enthalten. Auch später müssen die Register im freien Zugriff verbleiben, da zum Beispiel die Systemsoftware (PCI-BIOS) die Informationen über die Adreßräume benötigt und eventuelle Statusinformationen abgefragt werden müssen.

Um herauszufinden, welche Karten nun am PCI-Bus angeschlossen sind, kann man die Vendor ID in jedem der im System vorhandenen Slots auslesen. Die Hostsystem muß daher Zugriffe auf die Konfigurationsbereiche nicht vorhandener Karten (ohne Fehlermeldung) unterstützen. Da für eine Vendor ID der Wert FFFFhex nicht erlaubt ist, reicht es daher völlig, wenn die Systemhardware bei Lesezugriffen auf den Konfigurations-Bereich nicht vorhandener Karten eben diesen Wert zurückliefert.

Jedes PCI-Gerät muß Schreibzugriffe auf reservierte oder nicht unterstützte Register ignorieren, d.h. der Schreibzugriff muß am PCI-Bus ohne Fehlermeldung abgeschlossen werden. Die Daten selbst werden allerdings von der Karte verworfen. Lesezugriffe auf reservierte oder nicht implementierte Register müssen ebenfalls ohne Fehlermeldung am Bus abgeschlossen, und ein Wert von 0000hex zurückgegeben werden.




Tabelle 34: Die vordefinierten Konfigurationsregister
Configuration Register Adress-Offset
Device ID Vendor ID 00h
Status Command 04h
Class Code Revision ID 08h
BIST Header Type Latency Timer Cache Line Size 0Ch
Base Address Registers 10h
14h
18h
1Ch
20h
24h
Reserved 28h
Reserved 2Ch
Expansion ROM Base Address 30h
Reserved 34h
Reserved 38h
Max_lat Min_Gnt Interrupt Pin Interrupt Line 3Ch


Tabelle 1 zeigt das Layout des von der PCI-SIG definierten 64Byte-Bereichs, das jede Karte unterstützen muß. Jedem PCI-Gerät ist es natürlich freigestellt, weitere gerätespezifischen Register im nachfolgenden Bereich abzulegen. Alle Felder, bestehend aus mehr als einem Byte, liegen dem Little Endian (Intel)-Format zugrunde, d.h. die niederwertigere Adresse beinhaltet auch das niederwertigere Byte. Die Treibersoftware muß mit bitcodierten Feldern sorgsam umgehen, da diese zumeist reservierte Bitpositionen für eine etwaige spätere Verwendung beinhalten. Die Software muß daher bei Lesezugriffen Masken verwenden um die verwendeten Bits zu extrahieren, und darf sich keinesfalls auf bestimmte Werte in reservierten Bits verlassen. Bei Schreibzugriffen muß die Software sicherstellen, daß die Werte von reservierten Bits nicht verändert werden, d.h. ihr Wert muß vorher ausgelesen und zum gewünschten Wert der anderen Bits 'gemerged' werden. Auch dieser von der PCI-SIG vordefinierte Bereich gliedert sich in zwei Bereiche. Der erste 16 Bytes lange Bereich gilt für alle Arten von PCI-Geräten und ist immer gleich. Den verbleibenden 48 Bytes können in Abhängigkeit von der Funktion des PCI-Geräts verschiedene Layouts zugewiesen werden. Das Feld 'Header Type' an Adresse 0Ehex legt fest, um welches Layout es sich in diesem zweiten Bereich handelt. Alle PCI-Geräte müssen die Register für Vendor ID, Device ID, Command und Status implementieren. Die Implementierung der anderen Register ist optional und hängt wieder einmal mehr von der Funktionalität des PCI-Geräts ab. Nicht implementierte Register können vom PCI-Gerät wie reservierte Register behandelt werden. Wenn allerdings ein PCI-Gerät eine Funktion beinhaltet, deren Verhalten durch eines der definierten Register festgelegt wird, so muß das PCI-Gerät dieses Register an der dafür definierten Stelle mit dem definierten Verhalten zur Verfügung stellen.

Bedeutung der Konfigurations-Register:

Der PCI-Bus hat das Potential, die Konfiguration von Zusatzkarten wesentlich zu vereinfachen. Dazu müssen aber diese PCI-Geräte bestimmte Funktionen (über Register) der Konfigurationssoftware (PCI-BIOS) zur Verfügung stellen. Im folgenden werden also die Register dieses vordefinierten Konfigurations-Bereichs vorgestellt. Das exakte Format der Register (d.h. die Anzahl der implementierten Bits usw.) hängt wiederrum vom jeweiligen PCI-Gerät ab. Jedoch müssen einige allgemeine Regeln beachtet werden. So müssen alle Register die Möglichkeit bieten, die eingestellten Werte zurückzulesen, und diese gelesenen Daten müssen einen Wert repräsentieren, den die Karte für die aktuellen Einstellungen gerade verwendet. Die Konfigurationsregister werden zur Konfiguration, Initialisierung und für die Behandlung schwerer Fehler verwendet, und sollten nur von der Initialisierungssoftware (d.h. PCI-BIOS) und eventuellen Fehlerbehandlungsroutinen verwendet werden. Die Gerätetreiber selbst sollen nur Zugriffe auf I/O und/oder Memory Bereiche verwenden, um gerätespezifische Register und damit die Funktionalität des PCI-Geräts zu manipulieren.

Die Identifikationsregister

Fünf Felder im vordefinierten Bereich sind für die Identifikation des Geräts vorgesehen. Alle PCI-Geräte müssen diese Felder unterstützen. Die Konfigurationssoftware kann somit leicht feststellen, welche Geräte am PCI-Bus des Rechners verfügbar sind. Alle diese Register sind 'read only'.

Vendor ID: Dieses Feld identifiziert den Hersteller des PCI-Geräts. Gültige Kennungen werden von der PCI-SIG an zahlende (!) Mitglieder vergeben, um eine Eindeutigkeit zu gewährleisten. Diese Herstellerkennung ist 16 Bit breit, wobei der Wert FFFFhex eine ungültige Vendor ID darstellt und vom Motherboard bei einem nicht belegten Slot zurückgeliefert wird. Anhand dieses Wertes kann also das PCI-BIOS unterscheiden, ob in dem betreffenden Slot eine Karte vorhanden ist oder nicht

Device ID: Dieses Feld identifiziert ein spezielles Gerät. Die Kennung wird vom Hersteller der Karte selbst vergeben, und dient zusammen mit der Vendor ID dem Softwaretreiber zum eindeutigen Erkennen eines PCI-Gerätes. Die Funktion FindPCIDevice eines PCI-BIOS macht von dieser Möglichkeit Gebrauch. Ein spezielles Problem ergibt sich aber bei der Verwendung von Standard Interface Chips wie dem PCI9060. Verwendet man nämlich z.B. auf einer selbst gebauten Karte einen solchen Interface Chip, so meldet sich dieser mit seiner eigenen Vendor und Device ID und zu einer eindeutigen Erkennung und Unterscheidung solcher Karten ist daher noch eine Subsystem Vendor ID sowie eine Subsystem Device ID notwendig. Mit diesem Thema wird sich zu einem späteren Zeitpunkt ein weiterer Artikel beschäftigen.

Revision ID: Dieses Register beinhaltet eine gerätespezifische Revisionsnummer. Der Wert selbst wird wieder vom Hersteller der Karte vergeben, wobei auch die Revisionsnummer 00hex erlaubt ist. Dieses Feld sollte daher als herstellerspezifische Erweiterung zur Device ID angesehen werden.

Header Type: Dieses Byte legt sowohl das Layout der Bytes 10hex bis 3Fhex des Konfigurationsbereiches fest, als auch die Information, ob das Gerät weitere Funktionen beinhaltet. Bit 7 dieses Registers wird dazu verwendet, um sogenannte multi function devices zu kennzeichnen. Ist das Bit 0, so handelt es sich bei diesem Gerät um ein single function device, ist es hingegen gesetzt, so hat dieses Gerät mehrere Funktionseinheiten. Bits 6..0 legen schließlich das Format des zweiten Teils des Konfigurationsbereiches fest. Das in Tabelle 1 gezeigt Layout gilt für Standard-Geräte und ist mit Header Type 00hex gekennzeichnet. Zur Zeit ist noch Header Type 01hex für PCI to PCI-Bridges definiert.

Class Code: Das Class Code Register ist ebenfalls nur 'read only' und wird dazu verwendet, um die 'Gattung' (die generelle Funktion) eines PCI-Gerätes festzulegen, sowie in einigen wenigen Fällen ein Register Interface. Das Register ist dazu in 3 Bytes unterteilt. Das oberste Byte (an Adresse 0Bhex) stellt die Basisklasse dar, welche die Funktion dieser Karte ganz allgemein beschreibt. Das mittlere Byte (an Adresse 0Ahex) ist eine Subklasse, welches die in der Basisklasse definierte Funktion genauer beschreibt. Im Falle von Basisklasse 02hex für Network Controller wird hier z.B. weiter zwischen Ethernet, Token Ring, FDDI und anderen Arten unterschieden. Das niedrigste Byte (an Adresse 09hex) ist schließlich das gerätespezifische Register Interface, über das geräteunabhängige Software mit dem Gerät kommunizieren kann. Tabelle 2 zeigt die definierten Werte für die Basisklasse. Der Class Code selbst ermöglicht überhaupt erst die Zusammenarbeit von geräteunabhängiger Software mit Standardkomponenten wie etwa einer Grafikkarte, da es eben eine Fülle an verschiedenen Herstellern (Vendor ID) und verschiedenen kompatiblen Grafikkarten (Device ID) gibt, die ein Treiber sonst alle implementieren müßte.




Tabelle 35: Die Definition der Basisklassen
Basisklasse Bedeutung
00h PCI-Grät wurde bereits vor der Definition der Class Codes gefertigt
01h Mass Storage controller
02h Network controller
03h Display controller
04h Multimedia device
05h Memory controller
06h Bridge controller
07h-FEh Reserved
FFh Gerät passt in keine andere Klasse


Die Basisklasse 00 hex

Diese Basisklasse wurde definiert, um Abwärtskompatibilität für diejenigen PCI-Geräte zu gewährleisten, die bereits vor der Definition der Basisklassen gebaut wurden. Neue Geräte dürfen diesen Wert nicht mehr verwenden, und ältere Geräte sollten soweit als möglich auf einen aussagekräftigeren Wert umsteigen. Für diese Basisklasse existieren nur zwei Subklassen, alle anderen Werte sind reserviert.



Basisklasse Subklasse Interface Bedeutung
00h 00h 00h Alle PCI-Geräte ausser VGA-kompatible Geräte
01h 00h VGA-kompatible Geräte


Die Basisklasse 01 hex (Mass Storage controller)

Diese Basisklasse wurde für alle möglichen Massenspeicher-Anwendungen definiert. Bisher sind für diese Geräte keinerlei Register Interfaces definiert



Basisklasse Subklasse Interface Bedeutung
01h 00h 00h SCSI-Bus Controller
01h 00h IDE Controller
02h 00h Floppy Disk Controller
03h 00h IPI Bus Controller
80h 00h Andere Storage Controller


Die Basisklasse 02 hex (Network controller

Diese Basisklasse wurde für die verschiedensten Arten an Netzwerkkarten definiert. Auch hier gibt es bisher noch keine definierten Register Interfaces.



Basisklasse Subklasse Interface Bedeutung
02h 00h 00h Ethernet Controller
01h 00h Token Ring Controller
02h 00h FDDI Controller
80h 00h Andere Netzwerk Controller


Die Basisklasse 03 hex (Display controller)

Diese Basisklasse wurde für alle Arten an Grafikkarten definiert. Dazu gibt es einige Subklassen, zu denen jeweils ein eigenes Register Interface existiert.



Basisklasse Subklasse Interface Bedeutung
03h 00h 00h VGA kompatibler Controller
01h 00h XGA Controller
80h 00h Andere Grafik Controller


Die Basisklasse 04 hex (Multimedia device)

Diese Basisklasse definiert die möglichen Typen von Multimedia Geräten,für die aber keinerlei spezifische Register Interfaces definiert sind.



Basisklasse Subklasse Interface Bedeutung
04h 00h 00h Video
01h 00h Audio
80h 00h Andere Multimedia Controller


Die Basisklasse 05 hex (Memory controller)

Diese Basisklasse definiert alle möglichen Arten an Memory controllern. Es sind keine spezifischen Register Interfaces definiert.



Basisklasse Subklasse Interface Bedeutung
05h 00h 00h RAM
01h 00h FLASH
80h 00h Andere Memory Controller



Die Basisklasse 06 hex (Bridge Device)

Diese Basisklasse definiert alle möglichen Arten von "Bridge Devices". Eine solche PCI-Bridge ist ein PCI-Gerät, das PCI-Ressourcen (Memory ode I/O) von der einen Seite dieses Gerätes auf die andere Seite des Gerätes mappt. Es gibt mehrere Subklassen und es sind keine spezifischen Register Interfaces definiert.



Basisklasse Subklasse Interface Bedeutung
06h 00h 00h Host Bridge
01h 00h ISA Bridge
02h 00h EISA Bridge
03h 00h MC Bridge
04h 00h PCI to PCI Bridge
05h 00h PCMCIA Bridge
80h 00h Andere anderen Bridges



Die Kontrollregister

Command:
Über das Command Register können die verschiedenen Möglichkeiten des PCI-Gerätes über einzelne Bits freigeschalten werden, sofern das Gerät dies auch unterstützt. Das Command Register stellt auch eine einfache Kontrolle über das PCI-Gerät dar, ob es PCI-Cycles generieren beziehungsweise darauf antworten darf. Schreibt man eine 0 in das Command Register, so ist dieses Gerät mit Ausnahme von Zugriffen auf den Konfigurationsbereich logisch vom PCI-Bus getrennt. Alle PCI-Geräte müssen diese Funktionalität dem System zur Verfügung stellen. Die anderen Bits im Command Register können in Abhängigkeit der weiteren Funktionen und Möglichkeiten der betreffenden Karte wahlweise implementiert werden. Eine Karte, die z.B. keine I/O- Zugriffe erlaubt, wird auch das Setzen des entsprechenden Bits im Register nicht zulassen. Die PCI-Geräte beinhalten typisch den Wert 00hex nach dem Power up, d.h. die Funktionen werden erst vom PCI-BIOS freigegeben (oder auch nicht).





Tabelle 43: Bits im Command Register
Bit Bedeutung
0 IO Space
0: Gerät reagiert nicht auf I/O Zugriffe
1: Gerät reagiert auf I/O Zugriffe
1 Memory Space
0: Gerät reagiert nicht auf Memory Zugriffe
1: Gerät reagiert auf Memory Zugriffe
2 Bus Master
0: Gerät generiert keinerlei Zugriffe am PCI-Bus
1: Gerät darf als Busmaster auftreten
3 Special Cycles
0: Gerät ignoriert Special Cycle Operationen
1: Gerät reagiert auf Special Cycle Operationen
4 Memory Write and Invalidate Enable (als Busmaster)
0: Gerät benutzt als Busmaster nur Memory Write Befehle
1: Gerät darf Memory Write and Invalidate verwenden.
5 VGA Palette Snooping (Bit muß von allen VGA kompatiblen Geräten zur Verfügung gestellt werden)
0: Gerät behandelt Paletten-Befehle wie alle anderen
1: Gerät antwortet nicht auf Paletten-Befehle
6 Parity Error Response (Geräte, die auf Parity prüfen, müssen dieses Bit unterstützen; Geräte
müssen das Parity Bit immer generieren, auch wenn die Funktion über dieses Bit deaktiviert ist)
0: Gerät ignoriert Parity - Fehler
1: Gerät signalisiert und reagiert auf Parity - Fehler
7 Wait Cycle Control
0: Gerät arbeitet ohne Adress Data stepping
1: Gerät arbeitet mit Adress Data stepping
8 SERR# enable (Parity wird nur dann gemeldet, wenn auch dieses Bit gesetzt wurde)
0: Gerät darf an Pin /SERR keinen System Error signalisieren
1: Gerät darf an Pin /SERR System Error signalisieren
9 Fast Back-to-Back Enable (legt fest, ob das PCI-Gerät als Busmaster Transaktionen zu
verschiedenen Targets durchführen darf; die Initialisierungssoftware setzt dieses Bit, wenn
alle Targets Fast Back-to-Back capable signalisieren)
0: Gerät darf Fast Back-to-Back nicht verwenden
1: Gerät darf als Busmaster Fast Back-to-Back verwenden
10-15 reserviert


Status:
Das Status-Register wird dazu verwendet, um Informationen bezüglich PCI Bus - relevanter Vorgänge zur Verfügung zu stellen. Die PCI-Geräte müssen wiederrum nicht alle Bits implementieren, das hängt wieder von der Funktionalität der Karte selbst ab. Wenn eine Karte z.B. als Target agiert, aber niemals einen target abort signalisiert, wird auch dieses Bit von der Karte nicht verwendet werden. Lesezugriffe auf diese Register verlaufen normal, Schreibzugriffe auf dieses Register können Bits nur löschen, aber nicht setzen. Ein Bit in diesem Register wird gelöscht, wenn man an der jeweiligen Position eine 1 schreibt. Wenn man z.B. Bit 14 löschen möchte, ohne andere Register zu beeinträchtigen, so schreibt man in dieses Register den Wert 4000hex. Im Gegensatz dazu kann das PCI-Gerät selbst diese Bits nur setzen, aber nicht löschen.




Tabelle 44: Bits im Status Register
Bit Bedeutung
0 - 6 reserviert
7 Fast Back-to-Back Capable (zeigt an, ob das PCI-Gerät als Target Fast Back-to-Back
Transaktionen zu mehreren Targets verabeitet kann)
0: keine Fast Back-to-Back Transaktionen als Target möglich
1: Gerät versteht als Target auch Fast Back-to-Back Transaktionen
8 Data Parity Detected (wird nur signalisiert, wenn ein Paritätsfehler während eines
Busmaster-Zugriffs auftritt, und durch das Command Register auch freigeschalten wurde)
0: kein Parity Error bei Master Zugriff oder durch Command Register gesperrt
1: Parity Error bei Master Zugriff aufgetreten
9 - 10 DEVSEL timing (ein PCI-Target zeigt an Pin /DEVSEL an, daß es einen Zugriff auf eine
Adresse registriert hat, die sich in einem durch das Basisadreßregister zugewiesenem
Adreßbereich befindet; der Wert legt fest, zu welchem Zeitpunkt nach der Adreßphase der Pin
/DEVSEL von High nach Low wechselt.)
00: schnell (nach einem PCI-Taktzyklus)
01: mittel (nach zwei PCI-Taktzyklen)
10: langsam (nach drei PCI-Taktzyklen)
11: reserviert
11 Signaled Target Abort (zeigt an, wenn das PCI-Gerät selbst die laufende Transaktion am
PCI-Bus abbricht)
0: keine abgebrochene Transaktion
1: Gerät hat Transaktion mit 'Target Abort' abgebrochen
12 Received Target Abort (zeigt an, wenn das PCI-Gerät als Busmaster agiert und die laufende
Transaktion am PCI-Bus durch das Target abgebrochen wird)
0: keine abgebrochene Transaktion
1: Target hat Transaktion mit 'Target Abort' abgebrochen
13 Received Master Abort (zeigt an, wenn das PCI-Gerät als Busmaster agiert und die laufende
Transaktion mit Ausnahme von Special Cycle am PCI-Bus durch 'Master Abort' abgebrochen
wird - alle Busmaster-fähigen Geräte müssen dieses Bit implementieren)
0: keine abgebrochene Transaktion
1: Busmaster - Transaktion wurde durch 'Master Abort' abgebrochen
14 Signaled System Error (zeigt an, ob das Gerät an Pin /SERR einen System Error signalisiert hat)
0: kein System Error
1: System Error signalisiert
15 Detected Parity Error (zeigt an, ob das Gerät einen Parity Error erkannt hat - auch wenn das
Parity Handling durch das Command Register abgeschalten wurde)
0: kein Parity Error erkannt
1: Parity Error erkannt


Verschiedene Funktionen
Die nachfolgenden Register sind gerätespezifisch und müssen nur von denjenigen Karten zur Verfügung gestellt werden, die die beschriebene Funktionalität beinhalten. Für den Standardbetrieb sind diese Register nicht nötig.

Cache Line Size
Dieses Register legt die Größe der Cache Line in Langworten (32 Bit) fest. Dieses Register muß von allen busmasterfähigen Geräten zur Verfügung gestellt werden, die das Kommando Write and Invalidate generieren können. Geräte die am caching protocol beteiligt sind, verwenden dieses Register um herauszufinden, wann Burst-Zugriffe an den Cache Grenzen zu wiederholen sind. Wenn das Register auf 0 gesetzt ist, können die jeweiligen Geräte die PCI Cache Leitungen /SDONE und /SBO ignorieren. Geräte, die dieses Register zur Verfügung stellen, müssen dessen Wert bei Reset auf 0 setzen.

Latency Timer
Der Standardzugriff auf dem PCI-Bus ist der Burst. Ein Einzelzugriff ist somit nichts anderes als ein nach dem ersten Datenwort beendeter Burst. Sowohl der Master als auch der Slave (Target) können einen Bursttransfer abbrechen. Der Busmaster bricht dann einen Transfer ab, wenn ihm der Bus über /GNT entzogen wird, und seine interne Verzögerung (latency timer) abgelaufen ist. Dieses Register legt den Wert des Latency Timers für Busmaster fest, und muß daher auch von allen busmasterfähigen Geräten zur Verfügung gestellt werden, die mehr als zwei Datenphasen im Burst-Betrieb verarbeiten. Bei zwei oder weniger Datenphasen im Burst-Betrieb kann dieses Register auch als 'read-only' ausgeführt sein, der somit fixierte Wert sollte allerdings kleiner gleich 16 (bus clocks) sein. Eine durchaus verbreitete Implementierung sieht z.B. die untersten 3 Bits als 'read-only' vor, während die oberen 5 Bits beliebig verändert werden können. Dadurch ergibt sich eine Auflösung des Latency Timers von 8 bus clocks. Ist dieses Register allerdings nicht als reines 'read only'-Register ausgeführt, muß das PCI-Gerät dessen Wert bei Reset auf 0 setzen.

Built-in Self Test (BIST)
Dieses optionale Register dient zur Steuerung eines auf der PCI-Karte implementierten Eigentests, sofern dieser überhaupt vorhanden ist. PCI-Geräte die keinen Selbsttest zur Verfügung stellen, liefern in diesem Register den Wert 0 zurück. Ein Gerät, dessen Selbsttest gestartet wurde, darf den Normalbetrieb des PCI-Busses in keinster Weise beeinträchtigen.




Tabelle 45: Bits im BIST Register
Bit Bedeutung
7 BIST capable
0: Gerät unterstützt keinen Selbsttest
1: Gerät stellt einen eingebauten Selbsttest zur Verfügung
6 Start BIST
1: Selbsttest starten. Nach Beendigung des Tests wird dieses Bit vom PCI-Gerät wieder auf 0
gesetzt. Trifft dies auch nach dem Ablauf von 2 Sekunden nicht zu, so ist diese Karte als
defekt einzustufen.
5 - 4 reserviert
3 - 0 Completion code
0000: PCI-Gerät hat den Selbsttest bestanden.
Alle von 0000 verschiedenen Werte stellen gerätespezifische Fehlercodes dar.


Interrupt Line:
Dieses 8-bit breite Register wird vom Hostsystem dazu verwendet, die Information über den dem Gerät zugeteilten Hardware-Interrupt allgemein zur Verfügung zu stellen. Der Wert in diesem Register zeigt an, welcher Eingang des Interruptcontrollers mit dem Interrupt-Pin des PCI-Gerätes verbunden ist. Sowohl Gerätetreiber als auch das Betriebssystem selbst können diese Information dazu benutzen, um Interruptprioritäten und Vektorinformationen zu ermitteln. Die Werte in diesem Register hängen von der jeweilig verwendeten Systemarchitektur ab. Der Wert FFhex wird dann verwendet, wenn keine Zuordnung zum Interrupt Controller ermittelt werden konnte, oder wenn keine Verbindung zu diesem besteht. Vom PCI-Gerät selbst wird dieses Register nicht weiter beachtet.

Interrupt Pin:
Das Interrupt Pin Register kann nur gelesen werden. Über dieses Register teilt das PCI-Gerät dem Hostsystem mit, welche Interruptleitung des PCI-Busses es benutzt. PCI-Geräte, die keine Interrupts generieren, müssen dieses Register auf 0 setzen. (!nl!)



Tabelle 46: Interrupt Pin Register
Wert Bedeutung
0 PCI-Gerät generiert keine Interrupts
1 PCI-Gerät verwendet Interrupt Pin /INTA
2 PCI-Gerät verwendet Interrupt Pin /INTB
3 PCI-Gerät verwendet Interrupt Pin /INTC
4 PCI-Gerät verwendet Interrupt Pin /INTD
5-255 reserviert


MIN_GNT und MAX_LAT
Diese beiden 'read only' Register legen die Grenzwerte für das Setzen des Latency Timers (nur für den Busmaster-Betrieb nötig) fest. Beide Register haben hierbei eine Auflösung von 0,25 Mikrosekunden. Ein Wert von 0 zeigt an, daß dieses PCI-Gerät keine speziellen Anforderungen an die Konfiguration des Latency Timers stellt. MIN_GNT zeigt an, wie lange das PCI-Gerät für eine einzige Burst-Periode (nur eine Datenphase) in Bezug auf eine PCI-Taktrate von 33 MHz mindestens benötigt. Dem PCI-Gerät (als Busmaster) sollte also für mindestens diese Zeit der Bus durch den Bus Arbiter zugeteilt werden. MAX_LAT definiert, für wie lange das PCI-Gerät als Busmaster Zugriff auf den PCI-Bus erwirken möchte. Aus beiden Werten kann man dann einen geeigneten Wert für das Latency Timer Register wählen. Der Busmaster gibt den Bus ja erst dann zurück, wenn ihm der Bus Arbiter den PCI-Bus über /GNT entzieht, und der eigene latency timer abgelaufen ist. Ein Master darf sich aber nicht durch ständiges Aktivieren der /REQ-Leitung den Bus auf Dauer sichern, sondern nur für den aktuellen Zugriff benutzen. Der Algorithmus zur Arbitrierung ist in der PCI-Spezifikation allerdings nicht festgelegt. Die PCI-Geräte sollten daher auch solche Werte angeben, die eine effektive Verwendung sowohl des PCI-Busses als auch ihrer eigenen internen Ressourcen ermöglichen. (!nl!)

Basis-Addressen:
Die Basisadressen sind eine der wichtigsten Funktionen, um eine einfache Konfiguration und Einbindung von PCI-Geräten in das Rechnersystem zu gewährleisten. Die sechs Basisadreßregister enthalten die in der Initialisierungsphase (vom PCI-BIOS) vergebenen Adressen zum Zugriff auf die einzelnen Adreßräume des PCI-Gerätes. Bit 0 in diesen Adreßregistern kann nur gelesen werden und zeigt an, ob der Adreßraum im Memory-Bereich oder im I/O-Bereich liegt.

Memory-Bereiche: (!nl!) Register, die einen Memory-Bereich anfordern, müssen Bit 0 auf den Wert 0 setzen. Die Register selbst sind dabei entweder 32 bit oder aber auch 64 bit breit. Zur Auskodierung der Basisadresse verwendet das PCI-Gerät allerdings nur Bit 4 bis Bit 31 bzw. Bit63, d.h. Memory-Bereiche beginnen stets an 16 Byte - Grenzen. Die anderen Bits sind 'read only'. In Bit 3 des Registers wird mitgeteilt, ob der Adressbereich 'prefetchable' ist, und in Bit 2 und Bit 1 steht die gewünschte Art des Memory mappings in der Initialisierungsphase




Tabelle 47: Adreßregister für Memory-Bereiche
Bit Bedeutung
31(63)..4 Memory-Basisadresse (32 oder 64 bit breit)
3 Prefetchable
2 - 1 Type (Größe des Registers 32/64 bit, sowie gewünschter Adreßraum)
00: 32 bit breit, beliebig im 32 bit Adreßraum
01: 32 bit breit, Adreßraum unterhalb 1 MB
10: 64 bit breit, beliebig im 64 bit Adreßraum
11: reserviert
0 Memory Space Indicator = 0


I/O-Bereiche:
Register, die einen I/O-Bereich anfordern, sind immer 32 Bit breit und müssen in Bit 0 den Wert 1 zurückliefern. Bit 1 ist reserviert (read only) und muß immer gelöscht sein, alle anderen Bits (Bit 2 bis Bit 31) beschreiben schließlich die Adresse im I/O-Bereich, d.h. diese Bereiche beginnen stets an 4 Byte - Grenzen.




Tabelle 48: Adreßregister für IO-Bereiche
Bit Bedeutung
31..2 I/O-Basisadresse
1 reserviert (immer 0)
0 IO Space Indicator = 1


Insgesamt ist im Konfigurationsbereich Platz für sechs Adreßregister. Das erste Adreßregister beginnt immer an Offset 10hex. Die Lage des zweiten Adreßregisters hängt dann allerdings von der Größe (32/64 Bit) des ersten Registers ab, und beginnt demnach an Offset 14hex bzw. 18hex. Grafikkarten fordern typischerweise einen I/O-Bereich für die Kontrollregister und einen Memory-Bereich für den Grafikspeicher an. Wenn ein PCI-Gerät seine Kontrollregister in den Memory- und in den I/O-Bereich legen will, muß es dafür auch zwei Adreßregister 'spendieren'. Grundsätzlich sollten es alle PCI-Geräte ermöglichen, Kontrollregister in den Memory-Bereich zu mappen. Das ist aus der Unzulänglichkeit der PC's entstanden, die einen viel zu kleinen I/O-Bereich besitzen.

Anforderung von Adreßräumen
Um nun zu bestimmen, wieviel Speicher das jeweilige Gerät mittels der Adreßregister anfordert, beschreibt das Hostsystem jedes Adreßregister mit dem Wert FFFF'FFFFhex und liest danach das Register wieder zurück. Das PCI-Gerät liefert an jenen Bitpositionen 0 zurück, die es zur Auskodierung des gewünschten Speicherbereichs verwendet. Fordert das Gerät z.B. einen 1MB großen Memory-Bereich im 32-bit Adreßraum und einen 256 Byte großen I/O-Bereich an, so findet man nach dem Beschreiben mit FFFF'FFFFhex in einem Adreßregister FFF0'0000hex für den Memory-Bereich und in dem anderen FFFF'FF01hex für den I/O-Bereich vor. Das Hostsystem kann danach die Basisadressen für die jeweiligen PCI-Geräte vergeben. Ab diesem Zeitpunkt dekodieren die PCI-Geräte die Zugriffe dann selbst aus. Die Vergabe der Adreßräume beim Booten des Rechnersystems ist Aufgabe eines PCI-BIOS. Damit wären nun die wichtigsten Register vorgestellt und in der nächsten Folge können wir uns dann endlich mit der Definition und den Aufgaben eines ATARI PCI-BIOS beschäftigen. Die bereits in der ersten Folge bechriebene Möglichkeit von Erweiterungs-ROMs auf den Karten werden zu einem späteren Zeitpunkt behandelt. (!nl!

Milan-Homepage: http://www.milan-computer.de
Milan-Newsletter bestellen/abbestellen: http://www.milan-computer.de/html_d/letter.html

Letzte Änderung am 08. JuLi 1998, 13:07



Sonder-Edition PCI-BIOS, Part3 Stand: 09. 07. 1998


Das PCI-BIOS 3

Die Idee

Die Idee zum PCI-BIOS für die ATARI-kompatiblen Rechner entstand aus der Notwendigkeit der Zuordnung von Speicherbereichen zu den I/O- und Memory-Bereichen der PCI-Geräte. Auf PC's und Kompatiblen wird die PCI-Funktionalität über eine Bios-Erweiterung (das PCI-BIOS) standardisiert zur Verfügung gestellt. Das Bios hat zwei wesentliche Aufgaben. Zum einen die Bereitstellung einer Schnittstelle für die Software für den Zugriff auf die PCI-Geräte, zum anderen die Initialisierung und die Verteilung der Resourcen der einzelnen PCI-Geräte. Die Initialisierung läuft ohne Eingriffsmöglichkeiten des Benutzers direkt nach dem Reset ab.

Der Bootvorgang

Nach dem Reset fragt das Hostsystem zuerst das Vorhandensein der installierten PCI-Geräte ab. Dies erfolgt durch den Zugriff auf ein definiertes Register (Vendor ID). Zu diesem Zeitpunkt sind Zugriffe auf die noch unbekannten Karten nur mit den Befehlen Configuration Read und Configuration Write möglich. Nachdem das System herausbekommen hat, ob und wo Karten im Rechner stecken, werden bei allen PCI-Geräten die benötigten Adreßräume ermittelt. Dies geschieht wie bereits erwähnt durch das Schreiben des Wertes FFFF'FFFFhex in das jeweilige Adreßregister. Jedes PCI-Gerät kann maximal 6 Speicherbereiche anfordern. Dabei wird der jeweils benötigte Adreßbereich im Adreßregister bitweise ausmaskiert. Werden zur Kodierung zum Beispiel Bit 0 bis Bit 9 verwendet (auskodieren mit FFFF'FC00hex), ist der Bereich 2^10, also 1024 Bytes groß. Es besteht die Wahl zwischen I/O-gemappten und Memory-gemappten Bereichen (siehe Artikel in Ausgabe 5/98). Für ein PCI-Gerät unterscheiden sich die beiden Bereiche nur durch die unterschiedlichen Kommandos an /C/BE[0..3]. Als Antwort teilt das Hostsystem (BIOS) den einzelnen Geräten jeweils Adreßbereiche zu - sofern im Adreßraum genug Platz vorhanden ist. Hier hat z.B. der Hades im Vergleich zum PC einiges mehr zu bieten, da im PC der I/O-Bereich sehr begrenzt ist. Der Hades bietet in unserem Fall 256 MB I/O-Bereich und 512 MB Memory-Bereich an. Das sollte für den Anfang wirklich genügen ;-) Nach der Anmeldung sind die PCI-Geräte dann selbst für die Auskodierung ihrer Adreßräume verantwortlich. Die zugeteilten Adreßräume lassen sich für die späteren Zugriffe auf I/O- und Memory-Bereich aus den Konfigurationsregistern der Geräte auch wieder auslesen. In einem Register des definierten Konfigurationsbereiches (Interrupt Pin) wird von jedem PCI-Gerät die jeweils benutzte Interrupt-Leitung mitgeteilt. Das BIOS vergibt daraufhin einen der vorhandenen Interrupts und stellt die weitere Funktionalität über BIOS-Routinen zur Verfügung.

Wie sag' ich's meiner PCI-Karte

Der Treiber einer installierten PCI-Hardware durchläuft demnach folgende Schritte für die Initialisierung und anschließende Verwendung eines PCI-Boards:

*
Test auf vorhandenes PCI-BIOS und dessen Versionsnummer

 
*
PCI-Karte mit den Routinen find_pci_device oder find_pci_classcode suchen die Karte muß dann ausschließlich über das vom PCI-BIOS vergebene Geräte-Handle angesprochen werden.
 
*
entweder werden über get_resource die notwendigen Informationen für den direkten Zugriff auf die jeweiligen Speicherbereiche ausgelesen (der Treiber muß hierbei eventuelle Offsets, das Byte Ordering usw. selbst berücksichtigen), oder aber man verwendet die vom PCI-BIOS zur Verfügung gestellten Lese- und Schreib- Routinen für Memory und I/O- Bereiche
 
*
eventuell weitere kartenspezifische Informationen auslesen und entsprechend darauf reagieren
 
*
Karte schließlich über die Register in I/O- bzw. Memory-Bereich ansteuern Der weitere Zugriff auf die angeschlossenen PCI-Geräte nach der Initialisierung und Konfiguration erfolgt also über Memory- und/oder I/O- Zugriffe auf die entsprechend zugeteilten Adreßbereiche. Für diesen Zugriff wäre zwar kein spezielles Bios mehr erforderlich, aber das PCI-BIOS bietet aber eben auch hier eine einfache Zugriffsmöglichkeit auf diese Speicherbereiche/Register eines PCI-Gerätes, ohne sich um etwaige Offsets und Byte Orderings (Intel/Motorola) kümmern zu müssen.
 


Die PCI-Funktionen

Im folgenden wird also das Software-Interface beschrieben, das von den PCI-Gerätetreibern und anderer Systemsoftware genutzt werden kann, um Zugriff auf die PCI-Karten in ATARI-kompatiblen Systemen zu erhalten. Dieser Artikel soll zunächst das Konzept und die prinzipiellen Möglichkeiten aufzeigen, daher werden fürs Erste auch nicht sämtliche vom PCI-BIOS zur Verfügung gestellten Routinen beschrieben. Die restliche Funktionalität und weitere Einzelheiten können in der jeweils aktuellen Ausgabe der ATARI PCI BIOS Specification nachgelesen werden. Aller Vorraussicht nach wird man diese in naher Zukunft unter http://www.milan-computer.de finden. Das PCI-Bios installiert einen "_PCI"-Cookie, über den die BIOS-Routinen lokalisiert und angesprochen werden können. Für Programmierer gibt es hierfür bereits eine PCI-Library, die diese Aufgaben erledigt, und damit einen sehr einfachen Zugriff erlaubt. Die jeweils aktuellen Versionen des PCI-BIOS für den Hades und die entsprechende PCI-Library sind unter http://www.wvnet.at/privat/97021702/ zu finden. Weiters gibt es an dieser Stelle auch ein C-Beispielprogramm zur Verwendung dieser Library. Sobald die allgemein gültige ATARI PCI BIOS Specification im Internet erhältlich ist, wird es hier auch einen Link darauf geben. Beim Milan werden die BIOS-Routinen in einer der nächsten Versionen des Milan-TOS fix integriert sein, ein entsprechendes Programm im AUTO-Ordner ist im Gegensatz zum Hades also dann nicht mehr nötig. Ob und wann dies eventuell auch beim Hades der Fall sein wird, kann zu diesem Zeitpunkt noch nicht gesagt werden.
Jedem PCI-Gerät wird vom PCI-Bios eine Geräte-Handle zugewiesen, mit dem diese Geräte dann angesprochen werden können. Rückschlüsse vom Geräte-Handle auf den betreffenden PCI-Slot sind nicht möglich, und auch nicht erwünscht. Im folgenden werden jeweils die Aufrufe in Assembler und C dargestellt, die Informationen zu den verwendeten Parametern ergänzen sich gegenseitig. Sämtliche PCI-BIOS Routinen müssen im Supervisor-Modus aufgerufen werden.

Die PCI-BIOS Fehlercodes

Die folgende Fehlercodes können im Fehlerfall von den BIOS-Routinen zurückgeliefert werden.

Tabelle 1: Fehlercodes der BIOS-Routinen

Die folgenden beiden Fehlercodes werden zwar nicht von den BIOS-Routinen selbst verwendet, sind aber für eine PCI-Library reserviert.

Tabelle 2: Fehlercodes der PCI-Library


Find PCI device

Diese Funktion dient dazu, ein bestimmtes PCI-Gerät (eine PCI-Karte selbst kann ja wiederrum mehrere Funktionseinheiten = Geräte beherbergen) über Device und Vendor ID zu suchen. Damit findet ein Softwaretreiber also seine zugehörige Hardware, sofern sie auch im Rechner installiert wurde. Sollte eine Hardware mehrfach im System vorhanden sein, oder sich zwei Karten mit den gleichen IDs melden, so werden die einzelnen Karten über ihren Index abgefragt. Man beginnt dabei mit Index 0 und erhöht ihn dann solange, bis die Routine den Fehlercode PCI_DEVICE_NOT_FOUND zurückliefert.

Aufruf in Assembler:

Eingang:

D0.L Device ID in den Bits 31..16 (0 - $FFFF), Vendor ID in den Bits 15..0 (0 - $FFFE)
D1.W Index (0 bis Anzahl der PCI-Geräte mit dieser ID)

Ausgang:(!nl)

D0.L Geräte-Handle oder PCI-BIOS Fehlercode

Aufruf in C:

LONG handle = find_pci_device (ULONG id, UWORD index)

id: Device und Vendor ID des PCI-Gerätes

index: Index (bei mehreren gleichen Geräten)
Returnwert: positiv - Geräte-Handle

negativ - PCI-BIOS Fehlercode


Sonderfall:

mit Vendor ID FFFFhex kann man sämtliche im System vorhandene PCI-Geräte suchen, die Device ID wird in diesem Fall ignoriert.

Find PCI Classcode

Diese Funktion dient dazu, ein bestimmtes PCI-Gerät über ihren Classcode zu suchen. Mit dieser Funktion ist es z.B. möglich, die Grafikkarte zu erreichen, ohne den Hersteller oder die Device ID zu wissen. Diese Funktion führt natürlich nur dann zum Erfolg, wenn das PCI-Gerät auch einen der festgelegten Class Codes benutzt. Gibt es mehrere PCI-Geräte mit dem gleichen Classcode, so kann der Geräte-Treiber die weiteren Karten über ihren Index abfragen. Man beginnt dabei mit Index 0 an und erhöht ihn dann solange, bis die Routine den Fehlercode PCI_DEVICE_NOT_FOUND zurückliefert.

Aufruf in Assembler:

Eingang:
D0.L Classcode:
Bit 23..16 Base class (0 - $FF),
Bit 15..8 Sub class (0 - $FF),
Bit 7..0 Programming Interface (0)

Flags:

Bit 26 Base class 0:vergleichen 1:ignorieren

Bit 25 Sub class 0:vergleichen 1:ignorieren

Bit 24 Progr. interface: 0:vergleichen 1:ignorieren

Mit den Bits 24..26 kann man also angeben, welche Teile des Classcodes
mit den gefundenen PCI-Geräten übereinstimmen müssen
D1.W Index (0 - Anzahl der PCI-Geräte mit diesem Classcode)

Ausgang:

D0.L Geräte-Handle oder PCI-BIOS Fehlercode

Aufruf in C:

LONG handle = find_pci_classcode (ULONG class, UWORD index) 

class: Classcode des PCI-Gerätes
index: Index (bei mehreren gleichen Geräten)
Returnwert: positiv - Geräte-Handle
negativ - PCI-BIOS Fehlercode
Read Configuration {Byte|Word|Longword}

Diese drei Funktionen erlauben Lesezugriffe auf die Konfigurationsregister eines PCI-Gerätes, dessen Geräte-Handle zuvor mittels find_pci_device oder find_pci_classcode ermittelt wurde.

Aufruf in Assembler:

Eingang:

D0.L Geräte-Handle des gewünschten PCI-Gerätes
A0.L Zeiger auf Ergebnisvariable
D1.B Adresse des Konfigurationsregisters (0,1,2,... für Byte-Zugriffe)
D1.B Adresse des Konfigurationsregisters (0,2,4,... für Wort-Zugriffe)
D1.B Adresse des Konfigurationsregisters (0,4,8,... für Langwort-Zugriffe)

Ausgang:

D0.L PCI-BIOS Fehlercode
gelesene Daten stehen nach dem Aufruf in der Ergebnisvariable

Aufruf in C:

LONG errorcode = read_config_byte (LONG handle, UBYTE reg, UBYTE *adresse)
LONG errorcode = read_config_word (LONG handle, UBYTE reg, UWORD *adresse)
LONG errorcode = read_config_longword (LONG handle, UBYTE reg, ULONG *adresse)
handle: Geräte-Handle des gewählten PCI-Gerätes
reg: Adresse des Konfigurationsregisters
adresse: Zeiger auf Ergebnisvariable
Returnwert: PCI-BIOS Fehlercode
Read Configuration {Byte|Word|Longword} FAST

Diese drei Funktionen erlauben das Lesen von Registern im Konfigurationsbereich ohne aufwendige Fehler- und

Plausibilitätschecks und sind daher auch etwas schneller als ihre 3 Schwestern (daher besonders geeignet für Interrupt-Routinen und wenn man ganz genau weiß, was man tut ;-). Der Registerinhalt wird im Returnwert abgelegt. Dadurch können diese Funktionen in C-Programmen noch einfacher angewendet werden.

Aufruf in Assembler:

Eingang:

D0.L Geräte-Handle des gewünschten PCI-Gerätes
D1.B Adresse des Konfigurationsregisters (0,1,2,... für Byte-Zugriffe)
D1.B Adresse des Konfigurationsregisters (0,2,4,... für Wort-Zugriffe)
D1.B Adresse des Konfigurationsregisters (0,4,8,... für Langwort-Zugriffe)

Ausgang:

D0 gelesener Wert (8, 16 oder 32 Bits)
SR carry flag wird bei einem Fehler gesetzt (abhängig von der Implementation)

Aufruf in C:

UBYTE value = fast_read_config_byte (LONG handle, UBYTE reg)
UWORD value = fast_read_config_word (LONG handle, UBYTE reg)
ULONG value = fast_read_config_longword (LONG handle, UBYTE reg)
handle: Geräte-Handle des gewählten PCI-Gerätes
reg: Adresse des Konfigurationsregisters
Returnwert: Inhalt des Konfigurationsregisters

Beispiele:

1) device_vendor = fast_read_config_longword (handle, 0)
liefert in den Bits 0-15 die Vendor ID und in den Bits 16-31 die Device ID

2) vendor = fast_read_config_word (handle, 0); liefert die Vendor ID zurück

3) device = fast_read_config_word (handle, 2); liefert die Device ID zurück

4) timer = fast_read_config_byte (handle, 13); liefert schließlichen den Wert des Latency Timers

Write Configuration {Byte|Word|Longword}

Diese drei Routinen dienen zum Schreiben von Konfigurationsregistern eines PCI-Gerätes.

Aufruf in Assembler:

Eingang:

D0.L Geräte-Handle des gewünschten PCI-Gerätes
D1.B Adresse des Konfigurationsregisters (0,1,2,... für Byte-Zugriffe)
D1.B Adresse des Konfigurationsregisters (0,2,4,... für Wort-Zugriffe)
D1.B Adresse des Konfigurationsregisters (0,4,8,... für Langwort-Zugriffe)
D2 zu schreibender Registerwert (8/16/32 bits)

Ausgang:

D0.L PCI-BIOS Fehlercode

Aufruf in C:(!nl)

LONG errorcode = write_config_byte (LONG handle, UBYTE reg, UBYTE val)
LONG errorcode = write_config_word (LONG handle, UBYTE reg, UWORD val)
LONG errorcode = write_config_longword (LONG handle, UBYTE reg, ULONG val)

handle: Geräte-Handle des gewünschten PCI-Gerätes
reg: Adresse des Konfigurationsregisters
val: zu schreibender Registerwert
Returnwert: PCI-BIOS Fehlercode

Was passiert nun bei einem Interrupt?

Für jeden PCI-Interrupt kann es eine ganze Kette von Interrupt-Handlern geben, da sich ja auch mehrere PCI-Geräte den selben Interrupt teilen können (siehe auch Artikel in der Ausgabe 4/98). Über das Geräte-Handle kann nun ein Geräte-Treiber seinen eigenen Interrupt-Handler per hook_interrupt in die passende Kette eintragen lassen, ohne den dafür zuständigen Interrupt wissen zu müssen.
Tritt danach ein Interrupt auf, werden vom BIOS die in die Kette eingetragenen Handler der Reihe nach aufgerufen. Jeder Interrupt-Handler wird dabei mit jenem Parameter aufgerufen, der beim Aufruf von hook_interrupt spezifiziert wurde. Dieser Parameter ist vom Geräte-Treiber völlig frei wählbar, bei geeigneter Wahl könnte man mit einem einzigen Treiber auch mehrere (baugleiche) Geräte parallel bedienen. Die Interrupt-Handler müssen unmittelbar nach ihrem Aufruf prüfen, ob der Interrupt von der von ihnen betreuten Karte ausgelöst wurde, und dementsprechend reagieren. Das heißt, die Interruptursache ist zu beheben, und das entsprechende Bit des BIOS-internen Parameters ist zu setzen. Hat das entsprechende Gerät den Interrupt nicht ausgelöst, darf der Interrupt-Handler den BIOS-internen Parameter unter keinen Umständen verändern! Der Handler selbst ist als gewöhnliche Prozedur zu betrachten, d.h. auch in Assembler wird der Interrupt-Handler mit RTS abgeschlossen.

Aufruf in Assembler:

Eingang:

A0.L Parameter, der mittels hook_interrupt spezifiziert wurde (!nl)(!nl) D0.L BIOS-interner Parameter

Ausgang:

D0.L Bit 0 wird gesetzt, wenn der Interrupt von der betreuten PCI-Karte ausgelöst wurde, anderenfalls darf dieser Parameter nicht geändert werden

Aufruf in C:

LONG value = interrupt_handler (LONG *value, LONG internal)
value: Parameter, der mittels hook_interrupt spezifiziert wurde
internal: BIOS-interner Parameter
Returnwert: BIOS-interner Parameter
Hook Interrupt Vector


Hängt den Interrupt-Handler des Treibers in die Kette des entsprechenden Interrupt-Kanals. War vorher noch kein Handler in der Kette, wird auch der betreffende PCI-Interrupt im Hostsystem aktiviert. Nach Auftreten des jeweiligen Interrupts wird somit der angegebene Interrupt-Handler aufgerufen. Allerdings ist insbesondere darauf zu achten, daß die Interrupts auf dem PCI-Gerät erst nach dem Aufruf dieser Funktion aktiviert werden dürfen, da es sonst zu spurious interrupts kommen kann. Ist für das gewählte PCI-Gerät bereits ein Interrupt-Handler angemeldet, so erhält man einen entsprechenden PCI-BIOS Fehlercode. Es ist dringend davon abzuraten, per unhook_interrupt fremde Handler zu entfernen. Für diesen Zweck gibt es die Funktion get_card_used, über die eine Callback-Routine des fremden Treibers ermittelt werden kann. Der Aufruf dieser Callback-Routine gibt dem fremden Treiber dann die Möglichkeit, sich sauber und rückstandsfrei zu deinstallieren.

Aufruf in Assembler:

Eingang:

D0.L Geräte-Handle des gewünschten PCI-Gerätes
A0.L Zeiger auf den Interrupt-Handler des Treibers
A1.L Zeiger auf Parameter für Interrupt-Handler

Ausgang:

D0.L PCI-BIOS Fehlercode

Aufruf in C:

LONG errorcode = hook_interrupt (LONG handle, ULONG *routine, ULONG *parameter)
handle: Geräte-Handle des gewünschten PCI-Gerätes
routine: Zeiger auf den Interrupt-Handler des Treibers
parameter: Zeiger auf Parameter für Interrupt-Handler

dies ist ein vom Treiber frei zu wählender Wert, der bei jedem Aufruf des Handlers vom BIOS mitübergeben wird. Dadurch könnte ein einziger Interrupt-Handler auch mehrere baugleiche PCI-Geräte bedienen.

Returnwert: PCI-BIOS Fehlercode


Unhook Interrupt Vector(!nl)
Mit dieser Routine kann man einen mittels hook_interrupt angemeldeten Interrupt-Handler wieder entfernen. Der Treiber muß allerdings beachten, daß die Interrupts auf dem PCI-Gerät schon vor dem Aufruf dieser BIOS-Funktion deaktiviert werden müssen, da es sonst zu spurious interrupts kommen kann.

Aufruf in Assembler:

Eingang:

D0.L Geräte-Handle des gewünschten PCI-Gerätes

Ausgang:

D0.L PCI-BIOS Fehlercode

Aufruf in C:

LONG errorcode = unhook_interrupt (LONG handle)

handle: Geräte-Handle des gewünschten PCI-Gerätes
Returnwert: PCI-BIOS Fehlercode

Get Resource Data

Liefert sämtliche Infos zu den Resourcen einer PCI-Karte (bzw. eines PCI-Gerätes im Fall von Multifunktionskarten). Die zurückgelieferten Infos dürfen von den Geräte-Treibern keinesfalls verändert werden. Der Geräte-Treiber kann an Hand der angebotenen Informationen (Byte ordering usw.) die Karte dann direkt ansprechen. Eine weitere Möglichkeit ist die Verwendung der BIOS-Routinen read_mem_..., write_mem_..., read_io_... und write_io_..., wobei man sich dann um keinerlei Nebenbedingungen selbst kümmern muß. Die Routine liefert einen Zeiger auf den ersten Resource Deskriptor des gewünschten PCI-Gerätes. Der Geräte-Treiber kann dann die weiteren Deskriptoren über einen Offset (Länge eines Deskriptors) erreichen. Der letzte Deskriptor des Geräts ist wiederrum speziell markiert. Die Reihenfolge der Despriptoren entspricht derer der Basisadreßregister im PCI-Konfigurationsbereich. Ein PCI-Gerät kann auch mehrere Resourcen des gleichen Typs anfordern/verwenden.

Aufruf in Assembler:

Eingang:

D0.L Geräte-Handle des gewünschten PCI-Gerätes

Ausgang:

D0.L Zeiger auf ersten Resource Deskriptor bzw. PCI-BIOS Fehlercode

Aufruf in C:

LONG pointer = get_resource (LONG handle)
handle: Geräte-Handle des gewünschten PCI-Gerätes
Returnwert: positiv - Zeiger auf Resourcen-Informationen (erster Deskriptor)
negativ - PCI-BIOS Fehlercode


Tabelle 3: Aufbau eines Resource Deskriptors

BIOS-interne Daten, dürfen nicht verändert werden


Um den Deskriptor der nächsten Resource des PCI-Gerätes zu ermitteln, muß man zur Startadresse des aktuellen Deskriptors das Feld next addieren. Das Feld start gibt den Beginn der entsprechenden Resource im PCI-Adreßbereich an. Falls diese Resource nicht direkt ansprechbar sein sollte, so steht in diesem Feld die Adresse 0. Über length kann man schließlich die Länge dieser Resource bestimmen. Der PCI-Adreßbereich ist im allgemeinen nicht mit der von der CPU aus gesehenen Adresse gleichzusetzen. Der Adreß-Offset, der zur PCI-Adresse zu addieren ist, um die physikalische Adresse für die CPU zu ermitteln, ist im Feld offset abgelegt.Der Eintrag dmaoffset gibt schließlich den Offset an, der zur PCI-Adresse addiert werden muß, wenn es sich um DMA-Transfers handelt.

Tabelle 4: Die Flags im Resource-Deskriptor

Quellenverzeichnis:

[1] PCI Local Bus Specification Revision 2.0
[2] Elrad 3/97 und 4/97 (Bus Basics - Technische Grundlagen des PCI-Bus)
[3] PCI Special Interest Group P.O. Box 14070 OR 97214, Portland, USA http://www.pcisig.com
[4] AMCC - http://www.amcc.com
[5] PLX Technology - http://www.plxtech.com
[6] Xilinx - http://www.xilinx.com
[7] Lattice Semiconductor - http://www.latticesemi.com


Vielen Dank an Konstantin Woller für diese Infos.






Copyright © Robert Schaffner (doit@doitarchive.de)
Letzte Aktualisierung am 23. Mai 2004
Home Milan Hardware Milan PCI Hardware 1 Milan SCSI Hardware