Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende ÜberarbeitungNächste ÜberarbeitungBeide Seiten der Revision | ||
fpga:beispielpciegpio:start [2013-07-15 15:32] – tinner | fpga:beispielpciegpio:start [2013-07-22 10:20] – tinner | ||
---|---|---|---|
Zeile 54: | Zeile 54: | ||
===== GPIO Block in Qsys einbinden ===== | ===== GPIO Block in Qsys einbinden ===== | ||
+ | \\ | ||
+ | Damit der erstellte Block in [[ fpga: | ||
+ | \\ | ||
+ | {{: | ||
+ | \\ | ||
+ | Im sich öffnenden Fenster wählt man im Register //HDL Files// alle für den Block benötigten Files aus.\\ | ||
+ | \\ | ||
+ | {{: | ||
+ | \\ | ||
+ | Im Register //Signals// werden die vorhandenen Signal ihrer Funktion und ihrem Interface zugewiesen. Im hier beschriebenen Beispiele wird für das Signal isl_clk bei Interface //New Clock Input..//, für isl_reset_n //New Reset Input..// und für das Signal oslv_gpios //New Conduit..// ausgewählt. Für die übrigen Signale wählt man das bereits erstellt avalon_slave_0 aus. Danach muss noch die Spalte //Signal Type// ausgefüllt werden. Das ganze sollte dann so aussehen wie in der folgenden Abbildung: | ||
+ | \\ | ||
+ | {{: | ||
+ | \\ | ||
+ | Danach können mit einem klick auf //Remove Interfaces With No Signals// alle unnötigen Interfaces entfernt werden. Danach müssen beim avalon_slave_0 Interface die Clock und die Reset Quelle eingestellt werden. Ebenfalls bei der reset_sink muss ein Clock eingestellt werden. Damit sollten dann alle Fehlermeldungen unten im Fenster verschwunden sein. \\ | ||
+ | \\ | ||
+ | {{: | ||
+ | \\ | ||
+ | Im Register //Library Info// können noch einige Paramter eingestellt werden wie zum Beispiel der Name der Komponente.\\ | ||
+ | \\ | ||
+ | {{: | ||
+ | \\ | ||
+ | Mit Finish wird dann automatisch ein TCL Skript erstellt das im gleichen Ordner wie das Top File der Komponente abgelegt wird. Damit Qsys die erstellte Komponente beim erneuten Aufstarten wieder findet muss der Pfad unter // | ||
+ | \\ | ||
+ | {{: | ||
+ | \\ | ||
+ | ===== PCIe Block in Qsys erstellen ===== | ||
+ | \\ | ||
+ | Wird in Qsys ein Doppelklick auf den \\IP_Compiler for PCI Express\\ ausgeführt wird eine Komponente zum System hinzugefügt. Im erscheinenden Fenster wählt man folgende Einstellungen: | ||
+ | \\ | ||
+ | {{: | ||
+ | ===== System zusammenfügen ===== | ||
+ | \\ | ||
+ | Mit einem Doppelklick auf die selbst geschriebenen Komponenten kann auch sie eingefügt werden. Alle Anderen Komponenten ausser der PCIe und den nun erschienen GPIO Komponenten können gelöscht werden. Danach müssen noch Verbindungen gezogen und bestimmt werden welche Singale Exportiert werden. Dies geschieht mit einem Klick auf \\Click to Export\\. Das fertige System sollte dann folgendermassen aussehen: \\ | ||
+ | \\ | ||
+ | {{: | ||
+ | \\ | ||
+ | Wenn man nun unter // | ||
+ | |||
+ | ===== Einfügen des System in ein Design ===== | ||
+ | \\ | ||
+ | Qsys erstellt beim Generieren des Systems automatisch ein Blocksymbol das dann in einem .bdf eingefügt werden kann. Ansonsten hat man auch die Möglichkeit das Design einfach als Komponente in einem VHDL File einzubinden. Damit das System korrekt arbeitet müssen die Ein- und Ausgänge folgendermassen gesetzt werden: | ||
+ | * pcie_core_clk an einen 125Mhz Clock anschliessen. | ||
+ | * refclk an die zwei differentiellen Pins des Referenz-Clocks des PCIe Bus anschliessen. | ||
+ | * an den test_in Eingang konstant 0x00000000A8 anlegen. Die Bedeutung der einzelnen Bits kann dem Manual des PCIe IP Compiler entnommen werden. | ||
+ | * pcie_rstn an den Reset Pin des PCIe Bus anschliessen. | ||
+ | * rx_in an die zwei differentiellen RX_0 Pins des PCIe Bus anschliessen. | ||
+ | * tx_out an die zwei differentiellen TX_0 Pins des PCIe Bus anschliessen. | ||
+ | * reconfig_to_gxb konstant an 0x2 anschliessen dieser Wert kann wieder dem Manual entnommen werden. | ||
+ | * reconfig_gxb_clk an GND anschliessen. | ||
+ | * Die GPIO Ausgänge an beliebige Pins des FPGAs anschliessen. | ||
+ | |||
+ | Danach kann das System auf den FPGA geladen werden. Damit Linux das neue PCIe Device erkennt muss ein Neustart ausgeführt werden. Mit lspcie -v kann geprüft werden ob das System erkannt wurde. | ||
+ | |||
+ | ===== Linux Kernel Modul ===== | ||
+ | \\ | ||
+ | Damit die GPIOs aus dem User-Space z.B aus einem C Programm angesprochen werden können muss noch ein Kernel Modul geschrieben werden.\\ | ||
+ | In diesem Modul wird in einem ersten Schritt nach dem PCIe Device gesucht: | ||
+ | < | ||
+ | #include < | ||
+ | |||
+ | static struct pci_dev *pci_device = NULL; | ||
+ | |||
+ | pci_device = pci_get_device(0x1172, | ||
+ | if (pci_device == NULL){ | ||
+ | //Error | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Die beiden Parameter sind die Vendor- und Product ID des Devices die vorher in Qsys eingestellt wurden. Durch die Prüfung auf NULL kann festgestellt werden ob ein Device gefunden wurde. In einem nächsten Schritt wird das Device eingeschaltet: | ||
+ | < | ||
+ | error = pci_enable_device(pci_device); | ||
+ | if(error){ | ||
+ | //Error | ||
+ | } | ||
+ | </ | ||
+ | Hat dies geklappt kann mit folgendem Befehl der Speicherbereich für dieses Modul reserviert werden. Damit wird verhindert, dass andere Modul ebenfalls Zugriff auf diesen Speicherbereich erhalten. | ||
+ | < | ||
+ | error = pci_request_regions(pci_device, | ||
+ | if(error){ | ||
+ | //Error | ||
+ | } | ||
+ | </ | ||
+ | Mit folgendem Befehl erhält man nun einen void Pointer auf den Speicherbereich: | ||
+ | < | ||
+ | //Io map base address | ||
+ | base_addr = pci_iomap(pci_device, | ||
+ | if (base_addr == NULL){ | ||
+ | //Error | ||
+ | } | ||
+ | </ | ||
+ | Mit diesen hat man nun beliebig Zugriff auf die Register des FPGAs. Der zweite Parameter gibt an welche Memory-Bar man gerne hätte. Würde man dort z.B. 2 wählen erhält man einen Pointer auf das Config Register des PCIe Blocks. Im Fall des GPIO's Moduls könnte man z.B nun ein RTDM Echtzeitdevice erstellen. Hier ein Beispiel für die Header Datei: | ||
+ | < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | int init_fpga_gpio(void *bPtr); | ||
+ | void cleanup_fpga_gpio(void); | ||
+ | int fpga_gpio_open(struct rtdm_dev_context *context, rtdm_user_info_t *user_info, int oflags); | ||
+ | int fpga_gpio_close(struct rtdm_dev_context *context, rtdm_user_info_t *user_info); | ||
+ | int fpga_gpio_read(struct rtdm_dev_context *context, rtdm_user_info_t *user_info, void *buf, size_t nbyte); | ||
+ | int fpga_gpio_write(struct rtdm_dev_context *context, rtdm_user_info_t *user_info, const void *buf, size_t nbyte); | ||
+ | int fpga_gpio_ioctr(struct rtdm_dev_context *context, rtdm_user_info_t *user_info, unsigned int request, void __user *arg); | ||
+ | |||
+ | static struct rtdm_device fpgaGPIODevice = | ||
+ | { | ||
+ | .struct_version | ||
+ | |||
+ | .device_name | ||
+ | .device_flags | ||
+ | .context_size | ||
+ | |||
+ | .open_nrt = fpga_gpio_open, | ||
+ | |||
+ | .ops = { | ||
+ | .close_nrt = fpga_gpio_close, | ||
+ | .read_rt | ||
+ | .read_nrt | ||
+ | .write_rt = fpga_gpio_write, | ||
+ | .write_nrt = fpga_gpio_write, | ||
+ | .ioctl_rt = fpga_gpio_ioctr, | ||
+ | .ioctl_nrt = fpga_gpio_ioctr, | ||
+ | }, | ||
+ | |||
+ | .device_class | ||
+ | .driver_name | ||
+ | .driver_version | ||
+ | .peripheral_name | ||
+ | .provider_name | ||
+ | .proc_name | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | Dann im C-File wird in der Init Methode der Base-Pointer auf einen unsigned long long gesetzt damit bei einem Inkrement des Pointer die Adresse um 64Bit weiter springt so wie es im Avalon Interface Definiert wurde. Weiter wird das Device im System registriert: | ||
+ | < | ||
+ | static unsigned long long *gpioBasePtr = NULL; | ||
+ | |||
+ | int init_fpga_gpio(void *bPtr){ | ||
+ | gpioBasePtr = ((unsigned long long*) bPtr) + GPIO_INTERFACE_OFFSET; | ||
+ | if(rtdm_dev_register(& | ||
+ | printk( KERN_ERR " | ||
+ | return -1; | ||
+ | } | ||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | In der Write Methode kann nun z.B. der Wert der GPIO's geschrieben werden: | ||
+ | < | ||
+ | int fpga_gpio_write(struct rtdm_dev_context *context, rtdm_user_info_t *user_info, const void *buf, size_t nbyte){ | ||
+ | iowrite16(((unsigned short*)buf)[0], | ||
+ | return nbyte; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ |