Forum: FPGA, VHDL & Co. von PCIe Board in PC Arbeitsspeicher schreiben


von KDC (Gast)


Lesenswert?

Hallo,

vielleicht eine ziemlich blöde Frage, weiß ich nicht. Aber ich stehe vor 
einem (kleinen?) Problem.

Ich habe hier ein PCIe Dev Kit von Altera und soll u. A. Daten vom FPGA 
an den PC senden. Nachdem ich mir die DMA Beispiele angesehen und die 
Dokus gelesen habe ist meine Überlegugn nun, ob es nicht auch ohne DMA 
geht.
Der ganze DMA Kram (zumindest in der vorhandenen Beispielanwendung) wird 
im Design recht groß und ich steige da noch nicht ganz durch (kann sich 
ja noch ändern).

Das Prinzip das hier benutzt wird (Daten vom PC in ein DMA Register im 
FPGA schreiben, dieser wird dadurch gestartet und schickt die Daten aus 
dem angegebene Adressbereich an den ebenfalls angegebenen Adressbereich 
des PCs) klingt für mich simpel.

Nun ist die Frage, ob ich das auch ohne DMA (Controller) realisieren 
könnte. Dann würde doc hschätzungsweise die CPU sich damit rumschlagen 
müssen!? Wäre kein Problem. Die bestehende anwendung läuft auf einem 
Pentium I, wird aber durch nen Core2 ersetzt. Was ich damit sagen will, 
die CPU sollte dafür genügend reserven haben.

Ich kann bisher Daten in beide Richtungen senden (nutze bisher Windriver 
von Jungo, war dabei, bin aber nicht der Softwareexperte und habe daher 
bisher nur damit rumprobiert, allerdings scheint mir das ganze nur 
bedingt flexibel zu sein). D. h. ich kann mit dem Windriver Daten ans 
PCIe Board senden, dort wertet sie meine Logik aus und antwortet z.B. 
darauf.

Nun würde ich halt gerne, nachdem der PC den Befehl gesendet hat, 
größere Datenmengen (im niedrigen MB Bereich) an den PC senden.
DMA kam mir da natürlich als erstes in den Sinn, scheint mir aber recht 
kompliziert zu implementieren zu sein.

Jetzt aber mal zu der eigentlichen Frage:
1. Ist es mein beschriebenes vorgehen (ohne DMA Controler) möglich? Also 
Daten vom PCIe Board an Speicherbereiche des PCs zu schicken und diese 
Dort (nachdem z.B. nen Interrupt ausgelöst wurde) auszulesen?

2. Wie reserviere ich für soetwas einen Speicherbereich am PC? Mei 
nerster Gedanke was malloc(). allerdings wird dies wohl so einfach nicht 
gehen, der Speicherbereich wird ja für das laufende Programm reserviert 
und ja auch nur im virtuellen Speicher freigegeben.

3. Falls es so funktioniert wie ich es vor habe, wie sieht es mit 
Geschwindigkeit aus?

4. Welche Alternativen hätte ich?


Vielleicht kann mir da ja jemand helfen, vielleicht bin ich ja auch 
völlig auf dem Holzweg.

KDC

von KDC (Gast)


Lesenswert?

Ich hab da wohl eben ein wenig Halbwissen von DMA durcheinander 
gebracht. Die eigentlichen Fragen bleiben aber bestehen.

von cfgardiner (Gast)


Lesenswert?

Hallo KDC,

also vorab, ich denke, wenn Du so etwas noch nie gemacht hast, brauchst 
Du ca. 4 Wochen bis das alles läuft wie Du es haben willst. Vielleicht 
etwas schneller wenn Du das alles mit Junko machen willst. Du brauchst 
aber auf jeden Fall einen Gerätetreiber.

Zu Deiner eigentlichen Frage, ob Du 'einfach' vom End-Point ins PC 
schreiben kannst, leider nein. Du schreibst ohnehin nie zum CPU sondern 
der MEM Schreib- oder Lesezugriff von Deinem End-Point wird vom Chipset 
(z.B. Intel ICH, I/O Controller Host) abgefangen und ins Systemspeicher 
umgeleitet.

Nächster Punkt, es muss vorher ein Zielbereich im System Speicher 
gelockt werden (sonst kriegs't einen schönen Blue Screen o.ä). Hier gibt 
es verscheiden Ansätze. Hängt z.B. davon ab, ob Applikations (User Mode) 
SW auch auf die Adressen zugreifen soll/darf oder nicht. So oder so 
(User Mode oder Kernel mode only) muss dann die sog. Scatter/Gather 
List, die den Zielbereich im Speicher beschreibt, an Dein DMA Controller 
übermittelt werden. Dein HW kann dann diese Adressen verwenden.

Wenn Du Windows im Sinn hast, würde ich der umfangreiche WDK (Windows 
Driver Kit) von der MS Webseite holen und ein sog. KMDF (Kernel Mode 
Driver Framework) Treiber schreiben. Es gibt einige Beispiele auch für 
PCI.

Wenn Du Linux im Sinn hast, ist m.E. die Information leider recht 
verteilt. Ein guter Startpunkt ist der Rubini Buch, "Linux Devce 
Drivers". Es gibt sogar irgendwo eine Online version (google, wikipedia 
etc.).

Die Grundkonzepte zwischen Win und Linux sind recht ähnlich.

Zusammengefasst: Wenn Dein End-Point ein DMA Master sein will, brauchst 
Du leider unbedingt einen Gerätetreiber. Ich denke bis das Fundament 
steht hast Du schon so ca. 500 'C'-Codezeilen zu schreiben. Das Testen 
kann nervig sein, vor allem unter MS.

Bzgl. des Durchsatzes ohne DMA. Das geht nur wenn die CPU auf Deine 
Karte schreibt. Hier wirdst Du kaum über 2,5 MByte (typ. Response Zeit 
von 1.6 us für ein DWord) beim Lesen bzw. 10 MB beim Schreiben kommen 
(schneller weil keine Completions).

Viel Erfolg,
Charles

von KDC (Gast)


Lesenswert?

Hi Charles,

also erstmal zum Gerätetreiber:
Da nutze ich zur Zeit halt den Jungo Treiber, der auch DMA kann. Das 
Dumme ist nur, dass Altera sowohl von der Hardware als auch vo nder 
software her keinen Quelltext für die DMA Beispiele zur Verfügung stellt 
bzw. gibt es für das einzige offene VHDL Beispiel keine spezielle 
Software die mit dem Beispiel zurechtkommt.

Es ist so, dass ich das ganze für meine Diplomarbeit brauche. Bisher 
habe ich wie gesagt den PCIe Core am laufen, kann auch Daten schreiben 
(Memory Write Request) und Lesen (Memory Read Request + Completion). 
Dabei komme ich tatsächlich auf geschwindigkeiten im niedrigen MB/s 
Bereich.

Die Sache ist die, dass ich für den Treiber eigentlich gar nicht 
zuständig sein sollte, das sollte ein anderer Diplomand machen. 
Allerdings ist der noch nicht verfügbar. Die Anwendung soll im Endeffekt 
utner Linux laufen.

Da ich aber im Endeffekt mit der Geschichte weniger zu tun haben 
sollte/wollte, habe ich unter windows den Jungo Treiber verwendet, auf 
dem basieren auch die Altera Beispiele und man brauchte helt nicht viel 
machen und er lief out-of-the-Box mit einigen Beispielen (dummerweise 
kein offenes DMA Beispiel).
Linux soll ja im prinzip vom Treiberschreiben einfacher sein. Da gibt es 
auch einen OpenSource Treiber von einem anderen User, der aber nicht 
vollständig ist. Könnte ich aber zur Not drauf aufbauen.

Zum DMA:
Das mit den Adressbereichen und dem freigeben ist mir schon klar, kann 
ja nicht einfach mal eben irgendwelche wichtigen/benutzten 
Speicherbereiche platt machen. Bluescreen ich komme! Meine Frage 
diesbezügliche wäre ja, ob und wie ich die Bereiche für externe Zugriffe 
freigeben könnte.

Ich habe das mit dem DMA bisher so verstanden: Ich schieb dem FPGA Infos 
in sein Register (Quell Adresse, Ziel Adresse, Länge, etc.) und sag ihm 
er soll den Transfer starten. Und er überträgt dann die Daten als Memory 
Write Request mit der entsprechenden PC Adresse die Daten an den PC.

Mein Problem ist, dass ich mir gerne den ganzen Kram an DMA Logik im 
FPGA sparen würde (sofern möglich) da ich nur in Richtung PC senden 
möchte, und das auch "nur" 4MB und auch nicht in zerstückelte 
Speicherbereiche sondern am Stück. Vier Wochen wären kein Problem ^^

Meine Frage ist halt, ob/wie ich den Speicherbereich am PC freigeben 
könnte, ob der Ansatz so funktioniert (also im Endeffekt nur Daten vom 
FPGA aus an die Speicherbereiche am PC schicken, die Bereiche hab ich 
dem FPGA natürlich vorher mitgeteilt) und welche Geschwindigkeit dabei 
zu erzielen ist.


Ich hab mit mit dem "Software" Logic-Analyzer halt das angesehen, was 
das DMA Beispiel im DMA Transfer macht, und das war nur TLPs (Memory 
Write Req) senden.

von Andreas (Gast)


Lesenswert?

Hallo,

Ich glaube Du haust da zwei Sachen drucheinander,
wenn dein PCIe Core einen Memorywrite initiert IST das ein DMA. Da 
beisst sich Maus keinen Faden ab.

Ich glaube wo Du dich vor fürchtest ist Scatter/Gather sprich eine Liste 
von (4kB) Blöcken abzuarbeiten, welche Dir der Treiber zur Verfügung 
stellen muss...

Scatter/Gather ist sicherlich guter Stil, jedoch nicht unbedingt 
notwendig. Wenn Du einen Treiber baust der Dir bei Systemstart einen 
entsprechend grossen Speicherbereich alozierst, dann bekommst Du den (im 
Normalfall) auch am Stück zur Verfügung gestellt.

Du musst Dir dann noch die physikalische Adresse zu deinem 
Speicherbereich geben lassen, dafür gibt es Betriebsystemfunktionen.

Der rest läuft dann wie Du schon gesagt hast, über einfache Writes.
Die zu erzielende Geschwindigkeit ist dabei höher als bei 
Scatter/Gather, da Dir du keine Zeit zum Übertragen von Tabellen 
benötigst.

Gruß

Andreas

von cfgardiner (Gast)


Lesenswert?

Hi KDC,

mir ist immer noch nicht ganz klar wie Eure gesamte Arbeit ausschauen 
soll. Kommt dann noch eine Applikation SW dazu, die die Daten irgendwie 
visualisieren/auswerten soll oder was macht Ihr damit.

Denn, normalerweise wird der Datenverkehr immer von einer 
Applikation-SW, auch natürlich Demon, Dienst o.ä, eingeleitet. Dein 
Hardware kann von sich aus eigentlich nur über Interrupts eine 
Betriebssystem aktivität auslösen aber da wird es richtig aufwendig. 
Falls Dein HW Daten im Speicher sozusagen auf Vorrat halten soll, dann 
wird in der Regel ein Treiber vorher einen Speicherbereich reservieren 
(z.b. kmalloc) und die Adresse von diesem Puffer in die HW schreiben.

Das Gesamtsystem würde aber eher so ausschauen.
Eine Applikations-SW (auch Demon natürlich) hat z.B.
fH = open("/dev/irgendwas", PARAMS);
BytesRead = read(fH, buf, sizeof(buf));

Das System weiss welcher Treiber sich hinter "/dev/irgendwas" verbirgt. 
Dein Treiber hat einen Callback vereinbart der vom System aufgerufen 
wird als Folge des 'read' Befehls oben. Als Parameter bekommt er einen 
Zeiger auf 'buf', einen Puffer im User Space.

Der Treiber reserviert dann normalerweise Kernelspeicher (kmalloc) und 
übergibt diese Adresse an Dein HW. Wenn der HW signalisiert hat, dass er 
fertig ist (Interrupt, Polling eines Status Regs etc. etc.) kopiert der 
Kernel die Daten nach '*buf' mit copy_to_user() und der read() Befehl 
wird abgeschlossen.


Was ich zeigen will ist, ein DMA Treiber ist alles andere als trivial. 
Meine persönliche Meinung ist, wenn der Treiber eh von einem Komilitonen 
geschrieben werden soll und Dein Thema eigentlich der HW ist, 
konzentriere Dich doch lieber auf einer vernünftigen Simulation für Dein 
Konzept. Die Altera Umgebung ist doch diesbzgl. gar nicht so schlecht. 
Die gewünschte Übertragungsrate wirdst Du auf jeden Fall im Zielsystem 
erreichen. Mit DMA kommst Du problemlos in Richtung 100 MByte vielleicht 
sogar bis hinauf zu ca. 180 MByte oder so (x1 PCIe). Letzendlich muss 
nur der Treiber Dein HW schnell genug mit Zieladressen versorgen.

Ein Vorschlag für Dein DMA Controller wäre ein 64bit x N 
Zieladressen-FIFO, der vom Treiber periodisch gefüllt wird. 64 Bit weil 
48 Bit Start-Adresse (IA64 Hardware) + Grösse des Zielbereichs. Du 
spezifizierst dann Deinen Komilitonen was die maximale Bereichsgrösse 
ist, die Du bearbeiten kannst (z.B. 32 KByte oder 64 KByte). Dein 
Controller holt sich dann sequentiell Ziel-Deskriptoren aus diesem FIFO 
und beschreibt sie mit posted PCIe Pakete. Du brauchst natürlich auch 
ein Signalisierungsmechanismus wenn ein Bereich abgeschlossen ist 
(Interrupt, Register Polling, Stempel an eine andere Speicheradresse etc 
etc.)

Nochmals, viel Erfolg
Charles

von KDC (Gast)


Lesenswert?

Danke ihr beiden!

Das hat mir schon einiges gebracht, in dem es mich im Endeffekt 
bestätigt hat, dass meine grundsätzliche Idee nicht ganz bescheuert war 
;)

Die Anwendung gibt im Endeffekt gelegentlich (natürlich von einer 
Software gesteuert) Befehle an eine Logik, die damit einen Testchip 
beschaltet. Irgendwann ist der Punkt erreicht, wo der Speicher im 
Testchip ausgelesen werden soll. Das sind maximal 4 MB.

Daher reicht es mir im Endeffekt, Daten als normalen Memory Write 
Request an das FPGA zu schicken, und halt nach einiger Zeit(dann aber 
halt auch möglichst schnell) die Daten aus dem Chip Speicher zu lesen.

Wie gesagt, der Rest funktioniert soweit ganz gut, ich würde jetzt halt 
nur gerne den Abtransport der Daten realisieren. Dazu nochmal meine 
überlegungen:
1. PC reserviert Speicher.
2. PC Teilt FPGA die Adresse des reservierten Speichers mit
3. FPGA sendet die Daten (sofern PCIe Core sagt es sei Ready) 
hintereinander weg an die (natürlich mit Inkrementierung) erhaltene 
Speicheradresse
4. Wenn das FPGA fertig ist sendet es eine MSI mit Interrupt an den PC
5. Wenn der PC den Interrupt empfängt (alternativ Polling) holt die 
Software sich die Daten und wertet sie aus/speichert sie in eine Datei

Das war im Prinzip so, wie ich es mir vorgestellt hatte, und wie ihr es 
auch beschrieben habt, oder?

Dann noch zwei kurze Fragen dazu:
1. Wie groß können die reservierten Speicherbereiche sein?
2. Wenn ich es z. B. mit kmalloc mache, habe ich dann die tatsächliche 
physikalische Adresse? Oder wie komme ich da dran? Und der Chipsatz/PCIe 
Bus kann einfach in den Speicherbereich schreiben auch wenn ich ihn (in 
der anwendung) reserviert habe? Reservierung ist also nur für das 
Betriebsystem?


Zum Thema Simulieren: Ja, von Altera gibt es da Simulationswerkzeuge, 
wobei mir eigentlich der praktische Test immer lieber ist (zumindest bei 
so einer Anwendung).

von cfgardiner (Gast)


Lesenswert?

Hi KDC,

Deine Punkt 1) bis 5) sind so in Ordnung. Nur nebenbei, MSI ist aus PCIe 
sicht kein Interrupt sondern ein Memory Write an eine vom System 
vorgegebene Adresse. Der 1 DWord Payload wird, bis auf den unteren acht 
Bits (der Vektor), auch vom System vorgegeben. Noch ein Punkt: 
Windows-XP (und früher) kann kein MSI. Ach ja noch ein Punkt, wenn Dir 
PCIe Kompatibilität im Sinne der Spec. ein Anliegen ist, musst Du auch 
Legacy Interrupts (INTA bis INTD Messages) unterstützen, wenn Du 
Interrupts implementierst. Das System 'entscheidet' dann welches Du 
benutzen darfst und trägt das in die Konfiguration Space ein.

kmalloc() reserviert physischen Speicher und zwar bis zu einer maximalen 
Größe von ca. 128 KByte (ja, Kilo). Für größere Bereiche gibt's 
vmalloc() aber dann musst Du (genauer gesagt Dein Treiber Kollege) 
letzendlich eine Scatter/Gather Liste der physischen Adressen anfordern 
und dies dann an den FPGA übertragen. vmalloc() bekommt einen virtuellen 
Speicherbereich. Ich denke, wenn Du ein S/G-Listen FIFO in Deinem DMA 
Kanal implementierst, bist Du hier sehr flexibel auch gegen späteren 
Änderungen im Treiber Konzept. Der Treiber muss Dein HW nur mit Adressen 
versorgen. Es ist dann egal, ob der Treiber mit einem grossem 
(virtuellen) Puffer, mit Wechsel Puffer oder noch was arbeitet. Dein HW 
arbeitet natürlich mit physischen Adressen.

Das mit dem Simulieren verstehe ich. Auch ich sehe natürlich lieber 
persönlich nach, ob etwas 'blinkt und rappelt' nur ab einer gewissen 
System Komplexität ist das leider nicht mehr effektiv. Vor allen die 
Fehleranalyse kann uferlos sein. M.E. hast Du diese Komplexitätsschwelle 
schon überschritten. Ich würde wenigstens zweigleisig fahren.

Grüße,
Charles

von KDC (Gast)


Lesenswert?

Das MSI kein Interrupt ist war mri schon klar, gibt je keine weiteren 
Signalleitungen sondern geht alles über die eine tx Leitung, somit muss 
das ja quasi ein normales Datenpaket mit speziellem Inhalt sein. Aber 
danke nochmal für den Hinweise.

Genauso dass XP kein MSI kann, hatte ich irgendwo schon gelesen, aber 
wieder verdrängt.

Ich bin grad dabei mich mal ein wenig mit Linux auseinander zu setzen. 
Es gibt ja dieses Buch aus dem O'Reilly Verlag welches kostenlos als pdf 
zur Verfügung steht über Linux Treiber.

Da steht übrigens drinne, dass mit free_pages (oder so) und order=9 
irgendwas um die größenordnung 512kB möglich sein soll. Bei so großen 
Bereichen muss man ja aber sicherlich drauf aufpassen, ob die wirklich 
allociert werden können wenn sie am Stück zur Verfügung stehen müssen.

Da gehts dann vielleicht, wie du ja auch vorschlägst, Richtung 
Scatter/Gather. Muss ich mir mal ansehen.

Und nochmal zur Simulation:

Ich hab bisher nur einmal diesen Altera BFM verwendet und ein Beispiel 
zu testen. Ist das wirklich viel effizienter das darüber zu machen als 
mit SignalTap sich einfach z.B. den Datenverkehr und einige andere 
Signale anzusehen? Wie gesagt, Simulieren habe ich bisher 
vernachlässigt, nur um einzelne Logik Blöcke zu testen habe ich das 
benützt.

von KDC (Gast)


Lesenswert?

Ein paar Fragen sind so heute beim lesen noch aufgetaucht:

1. Wie bekomme ich unter Windows nen zusammenhängenden physikalischen 
Adressbereich und dessen Adresse?
2. Kann ich per Memory Write Request ohne sonstige Einstellungen in den 
Speicher schreiben (vom FGPA aus) oder muss ich am PC dafür noch 
irgendwas vorbereiten? (ausser Bereich freigeben)
3. Wenn ich am PC in den Speicherbereich vom PCIe Board schreibe(also z. 
B. das erste Byte von Bar0) wird dass dann vom Root Complex automatisch 
in ein TLP umgewandelt oder muss ich da selber aktiv werden.

Ich glaub das wars. Windows deswegen, weil ich da halt mit Windriver von 
Jungo nen einfaches Toolkit habe, womit ich auch schon kleine 
Testsoftware geschrieben habe und was für mich, der sich eigentlich 
nicht um die Treiberseite kümmern soll, wesentlich simpler und 
unaufwendiger ist. Die dritte frage richtet sich aber doch eher an 
Linux, damit hab ich heute rumgespielt, aber leider kein Modul geladen 
bekommen :/

von cfgardiner (Gast)


Lesenswert?

Hi KDC,

mit den 512 KB wäre ich tatsächlich vorsichtig. Ich kenne nur die 
Angabe, dass kmalloc() bis 128 KByte spezifiziert ist. Kann schon sein, 
dass neuere Kernel Versionen diese Zahl noch mal erweitert haben. Habe 
ich mich ehrlich gesagt nicht drum gekümmert. Ich verwende meistens 
vmalloc(). Wenn kmalloc() nicht die gewünschte Anzahl zurückliefern 
kann, liefert er meines Wissens keine (retcode = 0).

Als Vergleich, unter Windows habe ich mal in einen Treiber ein Tracer 
eingebaut, der die Größe der einzelnen S/G-List Elementen ausgegeben 
hat. Gleich nach dem Laden habe ich 4 mal 20 MByte angefordert (für 4 
DMA Kanäle). Ich habe nie gesehen, dass ein S/G Element größer als 3 
Seiten (3 x 4K Byte) war. Ich bin noch nicht dazu gekommen, die Messung 
unter Linux zu wiederholen.

Bzgl. der Simulation, das Problem mit dem SignalTap ist, dass Du nur ein 
oder wenige Übertragungen anschauen kannst. Mit der Simulation, kannst 
Du z.B. eine ganze Regression Sequenz übers Wochenenende laufen lassen.

Es ist eine Weile her seitdem ich mir das Altera BFM angeschaut habe. 
Damals zumindest war es auf verilog bassiert, sah aber sehr vernünftig 
aus. Ich hoffe, dass Dein Simulator mixed language (VHDL/Verilog) kann.

Grüße,
Charles

von Christian L. (ijuz)


Lesenswert?

@KDC
2.) ohne IOMMU nicht, also generell nicht
3.) ja

Passend fuer die Anwendung ist
pci_alloc_consistent
wenn man frueh nach dem booten alloziiert bekommt man auf jeden Fall 2 
MB.

Bei entsprechender Fragmentierung bekommt man aber nicht mehr als 4kB.
(bzw. eine page)

von KDC (Gast)


Lesenswert?

Danke euch beiden für die Antworten! Wobei ich mir jetzt bei Frage drei 
nicht sicher bin mit deiner Antwort. Ich mach sowas auch, aber auf 
"oder-Fragen" mit ja antworten ist immer etwas unglücklich ;) aber ich 
nehme mal an du meinst er macht daraus automatisch nen TLP (sonst 
hättest du ja evtl. noch mehr dazu geschrieben).

Wie gesagt, danke euch allen, das einzige was ich leider immer noch ned 
wei ist, wie ich bei Windows an freien physikalischen Speicher kommen. 
Aber das lässt sich ja hoffentlich rausfinden.

KDC

von Christian L. (ijuz)


Lesenswert?

@KDC
Ja, hab die Frage nicht ganz gelesen, wird natuerlich automatisch 
umgewandelt.

BTW. ich wuerde an deiner Stelle den Treiber selbst schreiben und auch 
etwas testcode, denn Du weisst nicht wielange es dauert bis der andere 
Diplomand mit seinem Zeug voran kommt.
Mach daraus einfach noch ein Kapitel in deiner DA.

von cfgardiner (Gast)


Lesenswert?

Hi KDC,

wie Du unter Windows an physischen Speicher kommst ist aus dem Stand 
nicht so einfach.
Aus dem User Mode (d.h. Borland, VisualXxx usw.) geht das überhaupt 
nicht. Windows versucht verzweiflet den unbedarften vor sich selbst zu 
schützen. Am Anfang hast Du erwähnt, Du würdest mit Jungo arbeiten. 
Falls Jungo die Einbindung von Funktionen aus der Windows WDK erlaubt, 
würde ich versuchen Speicher in der gewünschten Größe aus dem Kernel 
non-paged Pool anzufordern. Aber 4 MByte ist schon ziemlich Richtung 
Obergrenze für Speicher Blöcke, die aus dem Pool angefordert werden 
sollten.

Der Aufruf wäre ExAllocatePoolWithTag(type, nbytes, tag)
  type : NonPagedPool
  tag  : Ein Bezeichner, wenn ich mich recht errinnere, max 6 Zeichen 
und
         vermutlich auch noch als UNICODE string. Ich muesste 
nachschauen.

Rückgabe Wert ist ein Pointer auf eine Virtuelle Adresse. Du kannst
MmGetPhysicalAddress(PVOID baseAddr) verwenden um die physische Adresse 
zu bekommen.

Nachdem ich das gesagt habe, muss ich leider auch gleich sagen, 
Microsoft weisst darauf hin, dass diese Methode nicht für DMA verwendet 
werden soll. Ich habe es nur erwähnt, weil es zusammen mit Jungo 
funktionieren könnte (nie probiert, habe kein Jungo).

Wenn man es 'richtig' machen will, wird es wirklich kompliziert. Vergiss 
nícht, ich habe schon am Anfang von ca. vier Wochen gesprochen. Wenn Du 
wirklich ein eigener Bonsai Treiber schreiben willst, damit Du z.B. den 
TLP Inhalt in einem  Borland/VisualXxx Program sehen möchtest, hast Du 
einiges vor Dir.
Als erstes, WDK und SDK von der Microsoft Seite laden. Da ist die ganze 
Docu drin und viele viele Beispiele.

1) Für Dein Treiber (hier empfehle ich dringendst das KMDF Treiber Model 
(s. WDK), alles andere ist viel zu kompilziert) brauchst Du einen 
Registry Eintrag, damit Deine Applikations SW es überhaupt finden kann. 
Hierzu brauchst Du einen sog. GUID (globally unique Id). Die wird mit 
GENGUID.exe aus der SDK generiert. Den String hieraus kommt als 
'#DEFINE' in Deinen Treiber, Deine Applikation SW und Deine *.INF Datei

Dein Treiber wird übrigens immer mit dem WDK C-Compiler in einem DOS Box 
übersetzt. VisualStudio usw. kann das nicht.

2) In der Applikations Software (Borland/VisualXxx) verwendest Du
SetupDiGetClassDevs()
SetupDiEnumDeviceInterfaces()
SetupDiGetDeviceInterfaceDetail()

um einen Handle zu Deinem Treiber aufzubauen. (s. Beispiel in der SDK)

3) Mit
CreateFile()
ReadFile(drvHandle, buf,....)
kannst Du dann Daten von Deinem Treiber abholen.

---------------
Nun zum Treiber:
1) genauso wie unter Linux musst Du letzendlich einige Callbacks 
vereinbaren. Vor allem,
DriverEntry()
EVT_WDF_DRIVER_DEVICE_ADD
EVT_WDF_DEVICE_PREPARE_HARDWARE

In Deiner EVT_WDF_DEVICE_PREPARE_HARDWARE erfährst Du, wie Deine PCI 
Base Address Register gemapped sind

2) Mindestens ein IO-Queue Anlegen. Das Betribssystem leitet Deine 
ReadFile() Aufrufe hierhin. Die KMDF bietet dann einige Aufrufe um Queue 
Einträge abzuholen. Oder genauer, KMDF ruft Deine Callbacks auf, wenn 
Queue Einträge vorhanden sind.

3) Aus Deinem Queue Eintrag (IRP, I/O Request Packet in Windows Jargon) 
musst Du nun Zugang zum 'buf' aus Deinem ReadFile bekommen (am besten 
den sog. DMA_DIRECT Methode verwenden. Auch hier s. KMDF Docu)
Mit
WdfDmaTransactionInitializeUsingRequest(..CallbackFuerDma()...)
WdfRequestRetrieveOutputBuffer()
wird im Wesentlichen Deine DMA Übertragung gestartet.

Deine Routine CallbackFuerDma(), bekommt dann die Scatter/Gather Liste 
zu Deinem 'buf' im User Space vom System mitgeteilt und diese Adressen 
schreibst Du in Dein Hardware.

4) Mit WdfRequestComplete() wird das ganze abgeschlossen.

Das hier sind eigentlich nur als Stichwörter/Orientierungspunkte gemeint 
für Dein Studium der WDK Beispielen. Es lässt sich wirklich nicht 
umfassend auf nur einer HTML Seite beschreiben. Ab besten, den PLX 9x5x 
bzw. pcidrv Beispiele aus der WDK anschauen. Rechne, wie gesagt, mit 
einigen hundert Code Zeilen, viele Blue Screens und natürlich vier 
Wochen.

Grüße,
Charles

von Christian R. (supachris)


Lesenswert?

cfgardiner schrieb:
> Rechne, wie gesagt, mit
> einigen hundert Code Zeilen, viele Blue Screens und natürlich vier
> Wochen.

Nicht zu vergessen das Serial oder FireWire Kabel sowie ein 2. Rechner 
für den Kernel-Debugger :)

von cfgardiner (Gast)


Lesenswert?

Hi Christian,

ach für einen Bonsai Treiber braucht man das alles eigentlich nicht, 
auch wenn es MS vorschreibt. Klar man kommt vielleicht damit etwas 
schneller ans Ziel. Sofern man sich z.B. mit dem ganzen PnP Zeug nicht 
auseinander setzen muss, kann man schon darauf verzichten.

Wen ich z.B. für einen Demo oder auch kleineres Projekt o.ä einen 
Treiber schreibe, mache ich immer nur folgendes:
- Compilere mit Debug
- Verify aktivieren
- Nach einem Reboot, Crash Mini-Dumps mit WinDbg analysieren.

Die angemeckerte Zeilen sind meistens in der Nähe vom tatsächlichen 
Fehler, zumindest sofern man mit dem KMDF Model gearbeitet hat.

Klar, alles Abwägung.

Grüße,
Charles

von KDC (Gast)


Lesenswert?

Ich seh schon, zumindest nen komplett eigener Treiber droht mir überden 
Kopf zu wachsen. Bei Linux bekomme ich nicht mal ein Modul 
compiliert/geladen und an Windows will ich lieber gar nicht denken ;-)

Nunja, der Jungo Treiber hat eine funktion um Speicher für DMA zu 
reservieren, allerdings weiß ich nicht ob die für meine Zwecke 
funktioniert, das werde ich dann mal als nächstes testen.

Dann habe ich gehört, dass es hier im Haus auch schon ne 
treiberentwicklung gab, für eben dieses Board aber eine anderen 
Anwendung. Da bin ich auch dran mit den Code mal zu besorgen. Ansonsten 
muss doch Linux herhalten.

Ich schau mal was sich machen läßt.

von KDC (Gast)


Lesenswert?

So,

ich wollte mich nochmal bei allen bedanken, die mir geholfen haben.
Bin zwar was den DMA Teil angeht noch nicht fertig, aber das Prinzip 
funktioniert soweit super!

Und um noch ein bisschen Wissen beizusteuern:

mit __get_free_pages(GFP_KERNEL, 10) läßt sich unter Linux im Treiber 
ein zusammenhängender Speicher von 4 MB reservieren. Der aktuelle Kernel 
kann meines Wissens auch noch order (ist das zweite Argument, 2^n * 4kB) 
11, also 8 MB.

GFP_KERNEL geht in meinem Falle, da mein PCI (Express) Board 64 bit 
Adressen verwalten kann, falls das nicht geht muss man es um __GFP_DMA 
ergänzen, damit ist bei mir aber dann die zweite oder dritte Allozierung 
schief gegangen.

So, wie gesagt, vielen Dank euch allen nochmal!

KDC

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.