Dies ist eine alte Version des Dokuments!


PCI Express mit GPIO Interface

Einführung


Auf dieser Seite soll an einem Beispiel gezeigt werden wie GPIOs über eine PCIe Schnittstelle mit einem Linux Rechner angesteuert werden können. Das unten dargestellte Bild zeigt eine Übersicht über das gesamte System. Ein Linux Rechner ist über PCI Express (PCIe) mit einem FPGA verbunden. In diesem befindet sich zwei Blöcke einerseits der von Altera bereitgestellte IP Compiler und anderseits ein selbst geschriebener GPIO Block mit Avalon Interface.

GPIO Block

Als erstes soll genauer auf den GPIO Block eingegangen werden. Der Beispielcode kann hier heruntergeladen werden. Die folgende Signale bestimmen das Avalon Interface:

isl_clk :	IN STD_LOGIC;
isl_reset_n :	IN STD_LOGIC;
islv_avs_adress : IN STD_LOGIC_VECTOR(avs_adress_width_gpio_if-1 DOWNTO 0);
isl_avs_read :	IN STD_LOGIC;
isl_avs_write :	IN STD_LOGIC;
islv_avs_write_data : IN STD_LOGIC_VECTOR(avs_data_width_gpio_if-1 DOWNTO 0);
oslv_avs_read_data : OUT STD_LOGIC_VECTOR(avs_data_width_gpio_if-1 DOWNTO 0);

Dabei bestimmt die Konstante avs_adress_width_gpio_if die Anzahl Register des Blocks die angesprochen werden können. In diesem Beispiel ist der Wert 1 also können 2 Register angesprochen werden. Wird die Adressweite auf 2 gesetzt könne 4 Register angesprochen werden etc.
Die Konstante avs_data_width_gpio_if bestimmt die Breite der Register. Hier 64 Bit obwohl davon nur 16 verwendet werden. Dies erleichterst später den C-Code im Linux Treiber.
Die Daten vom Bus werden im folgend Codeabschnitt geschrieben:

IF isl_avs_write = '1' THEN
  CASE islv_avs_adress IS
    WHEN gpio_val_reg_address =>
      gpio_val_reg <= islv_avs_write_data(nr_of_gpios-1 DOWNTO 0);
    WHEN gpio_dir_reg_address =>		
      gpio_dir_reg <= islv_avs_write_data(nr_of_gpios-1 DOWNTO 0);
    WHEN OTHERS => null;		
  END CASE;
END IF;

Wenn also das write Signal high ist können je nach Wert des Adress Signals die Daten vom write_data Signal in einem anderen Register gespeichert werden.
Der Read-Teil funktioniert Analog dazu.
Danach müssen nur noch je nach Register Werten die Ausgänge gesetzt werden was mit folgendem Code gemacht werden kann:

FOR i IN 0 TO nr_of_gpios-1 LOOP
  IF gpio_dir_reg(i) = '0' THEN --output
    oslv_gpios(i) <= gpio_val_reg(i);
  ELSE --input
    oslv_gpios(i) <= 'Z';
    gpio_val_reg(i) <= oslv_gpios(i);
  END IF;
END LOOP;