Projekte.PCI-Tutorial-BTTipps (Struktur)


Praktische Tipps

Übersicht

Inbetriebnahme / Download des Designs

Die folgenden praktischen Tips zur Inbetriebnahme sind insbesondere dann interessant, wenn das FPGA direkt, also nicht über das ISP PROM auf dem Entwicklungsboard programmiert werden soll.

Im Rechner wird die PCI-Karte über den PCI-Bus mit Energie versorgt. Man kann das FPGA daher nicht konfigurieren, ohne den PC einzuschalten. Die Rechner, die beim Test zur Verfügung standen, erwarten jedoch beim Einschalten nach der Resetphase eine funktionierende PCI-Karte, deren Programmierung später durchaus geändert werden kann und dies auch vom PCI-BIOS nach einem Reset erkannt wird. Wird der Rechner jedoch mit einer nicht beriebsbereiten PCI-Karte gestartet, bleibt die Karte auch nach der Programmierung unerkannt.

Für dieses Problem gibt es zwei Lösungen:

  • Die FPGA-Konfiguration wird im PROM (in dem das Demo-Design des Kartenherstellers abgelegt ist!) gespeichert. Nach dem Aus- und Einschalten des Rechners wird das FPGA in der Bus-Resetphase mit dem PROM-Inhalt programmiert (Jumper auf Mode Select 1-2 stecken). Das PCI-BIOS findet dann eine funktionierende PCI-Karte vor.
     
  • Im PROM befindet sich das Demo-Design oder eine andere funktionierende Konfiguration. Das FPGA soll aber direkt programmiert werden. Dann kann man folgendermaßen vorgehen:
    1. Im ausgeschalteten Zustand des Rechners den Mode Select Jumper 1-2 schließen.
    2. Rechner einschalten. Die PCI-Karte wird mit dem Demo-Design konfiguriert und vom PCI-BIOS erkannt. Der Boot-Vorgang kann angehalten werden, wenn die Initialisierungsphase abgeschlossen ist und das Betriebssystem geladen werden soll. Lädt man das Betriebsystem, so kann unter Linux mit
           cat /proc/pci
      
      überprüft werden, ob die Karte tatsächlich vom System erkannt wurde. Dies ist Voraussetzung für die weiteren Schritte.
    3. Der Rechner bleibt eingeschaltet. Nun den Jumper Mode Select 1-2 entfernen und den Program-Taster (SW2) auf dem Entwicklungsboard drücken.
    4. Das FPGA mit dem eigenen Entwurf programmieren und den Computer neu starten, natürlich ohne ihn auszuschalten.
    5. In der Startphase meldet das PCI-BIOS, daß es eine veränderte Kartenkonfiguration gefunden hat, z.B.
          Updating ESCD... Succcess
      
      Nach dem Laden von Linux können wiederum mit
           cat /proc/pci
      
      die Parameter der PCI-Karte, dieses Mal aber mit dem eigenen Entwurf, abgefragt werden.
    Es bietet sich an, statt des Jumpers einen Taster anzuschließen, der während des Einschaltens des Rechners geschlossen gehalten wird.

Rechnerneustart nach Neuprogrammierung umgehen

Wie im Abschnitt "Der PCI Configuration Header" dargestellt, wird eine PCI-Karte nach dem Einschalten des Rechners durch das PCI-BIOS und ggf. später noch einmal durch das Betriebssystem konfiguriert, z.B. Ressourcen wie Anfangsadressen für die angeforderten Adressräume oder ein IRQ zugeteilt.
Bei der Neuprogrammierung des FPGAs gehen diese zur Bootzeit bzw. beim Laden des Betriebssystems im Konfigurationsraum der PCI-Karte eingetragenen Informationen verloren. Nach der Neuprogrammierung des FPGAs ist die PCI-Karte nicht mehr initialisiert und funktioniert im allgemeinen nicht mehr im laufenden System. Das Rechner muss neu gestartet werden, damit die PCI-Karte neu initialisiert wird.

Unter den folgenden Bedingungen kann jedoch ein Neustart umgangen werden (vom Trivialfall, bei dem die Karte keine Ressourcen belegt, einmal abgesehen):

  1. Im neuen VHDL-Entwurf sind die Einstellungen für den PCI Configuration Header (Datei cfg.vhd) unverändert geblieben.
  2. Für den Betrieb der PCI-Karte wird ein Kernelmodul verwendet (Normalfall).
  3. Die übrige Hardware-Konfiguration verändert sich nicht.
Der Trick besteht nun darin, sich die aktuelle Kartenkonfiguration vor der Neuprogrammierung des FPGAs zu merken und nach dem Download des neuen Designs wieder in den Konfigrationsraum der Karte zurückzuschreiben:
  1. Beim Laden des Kernelmoduls für die (durch Neustart nach Programmierung) korrekt initialisierte PCI-Karte wird der Konfigurationsraum des PCI-Geräts ausgelesen und in einer Datenstruktur des Kernelmoduls gespeichert.
  2. Der FPGA wird mit dem veränderten Entwurf programmiert; der Kerneltreiber bleibt geladen!
  3. Das Hilfsprogramm restore_config.c (siehe unten) veranlasst das Kernelmodul, die zuvor gesicherte Konfiguration in die Karte zu schreiben.

Erweiterungen des Kernelmoduls

Bei Initialisieren des Treibers wird am Ende der Funktion init_module() der gesamte Konfigurationsspeicher des PCI-Geräts gesichert. Man könnte sich natürlich auch nur die relevanten Register heraussuchen, aber so geht es einfacher. Das Wiederherstellen der Konfiguration wird über die ioctl()-Methode realisiert.
  /* ioctl()-Nummer fuer den Wiederherstellungsbefehl */
  #define IOCTL_RESOTRE_CONFIG 0

  /* globales Array fuer den Konfigurationsspeicher */
  u32 config_space[64];

  /* PCI-Geraete Verwaltungsstruktur */
  struct pci_dev *pci_dev;

  /* Wiederherstellen */
  static int xilinx_pci_ioctl (struct inode *inode, struct file *file,
                               unsigned int cmd, unsigned long arg)
  {
    int i;
  
    [... weitere ioctls ...]

    if (cmd == IOCTL_CONFIG_RESTORE)
    {
      for (i = 0; i < 64; i++)
        pci_write_config_dword (xilinx_dev.pci_dev, i * 4, 
                                xilinx_dev.config_space[i]);
      return 0;      
    }
    return -EIO;
  }

  /* ioctl-Funktion registrieren */
  static struct file_operations xilinx_pci_fops = 
  {  
    [...]
    ioctl : xilinx_pci_ioctl
  }; 
 

  /* Modul-Initialisierung */
  int init_module (void) 
  {
    int i;
 
    [...]
    for (i = 0; i < 64; i++)
      pci_read_config_dword (pci_dev, i * 4, config_space + i);
    [...]
  }

Das Programm restore_config.c

  #include <unistd.h>
  #include <fcntl.h>
  #include <stdio.h>
  #include <sys/ioctl.h>

  #define PCI_DEVICE "/dev/xilinx_pci"
  #define IOCTL_RESTORE_CONFIG 0

int main () 
{
  int pci_dev;
  
  pci_dev = open (PCI_DEVICE, O_RDWR, 0);
  if (pci_dev == -1)
  {
    printf ("Fehler beim Oeffnen des PCI-Devices %s.\n", PCI_DEVICE);
    return 0;
  }
  ioctl (pci_dev, IOCTL_RESTORE_CONFIG, NULL);
  return 0;
}

Umgang mit Kernelmodulen

Ein Kernel-Modul muss zwingend mit den beiden Präprozessor-Optionen -D__KERNEL__ und -DMODULE kompilert werden. Dies dient zur Freigabe bestimmter Datenstrukturen in den importierten, systemnahen Header-Dateien, z.B.
    gcc -Wall -O2 -D__KERNEL__ -DMODULE -c xilinx_pci.c
Allen Beispielprogrammen liegen passende Makefiles bei.

Bevor das Modul in den laufenden Kernel eingebunden werden kann, muss noch die Gerätedatei angelegt werden:

    mknod /dev/xilinx_pci c 240 0
    chmod a+rw /dev/xilinx_pci
Das 'c' steht für einen zeichenorientiertes Gerät (character), 240 ist die Major-Nummer und 0 die Minor-Nummer.
Das kompilierte Modul xilinx_pci.o wird mit
    insmod xilinx_pci.o
geladen (insert module). Aufschluss über erfolgreich geladene Module gibt das Kommando
    lsmod
oder auch
    cat /proc/modules
Für das Entladen des Moduls gibt es den Befehl
   rmmod xilinx_pci
Erst danach kann ein ggf. verändertes neu kompiliertes Modul erneut für das Gerät geladen werden.
Wertvolle Hilfen beim Debuggen sind die Log-Dateien
    /var/log/kern.log
    /var/log/dmesg
in denen die Fehler- und Statusmeldungen des Moduls (und die durch es verursachten) gespeichert werden.


Autor: gkemnitz, Letzte Änderung: 14.04.2011 15:09:59


 TU Clausthal 2020  Impressum