Entwicklung von Schnittstellenkarten auf Soil Xilinx Zynq 7000 für die Sprachaufzeichnung im analogen und digitalen Format



In diesem Artikel werden wir unsere Erfahrungen bei der Entwicklung von Schnittstellenkarten der Schnittstelleneinheit auf Basis des SoC ARM + FPGA Xilinx Zynq 7000 teilen. Die Karten wurden für die Aufzeichnung von Sprachsignalen im analogen und digitalen PRI / BRI-Format (ISDN, E1 / T1) entwickelt. Das endgültige Gerät selbst wird zur Aufzeichnung von Verhandlungen in der Zivilluftfahrt verwendet.

Eisen: Auswahl der Gerätehardwareplattform


Die Wahl der Hardwareplattform wurde durch die Unterstützung von PRI / BRI-Protokollen bestimmt, die nur auf der FPGA-Seite implementiert werden können. Mikrocontroller (MCUs) und Mikroprozessoren (MPUs) passten nicht.

Man könnte zwei Lösungen für dieses Problem wählen:

  1. Microblaze IP-Kernsynthese
  2. SoC Zynq-7000.

Wir haben uns für ein System auf einem Zynq 7000-Chip (SoC) entschieden, weil Es ist einfacher, Softwareanwendungen zu schreiben und bietet mehr Funktionen für aktuelle und zukünftige Aufgaben.

Insgesamt wurde im Rahmen des Projekts die folgende Liste von Eisen gesammelt:

1. Xilinx Zynq 7020 ( Mars-ZX3 und Mars EB1 )

Enclustra Mars ZX3 SOM

Enclustra Mars EB1 Baseboard

2. TI TLV320AIC34 ( tlv320aic34evm-k und USB-Motherboard).


Debugboard für tlv320aic34 (tlv320aic34evm-k)


USB-MODEVM-Erweiterungskarte für tlv320aic34evm-k

3. IDT82P2288 - PRI, XHFC-4SU - BRI-Mikroschaltungen, es gab keine Debugging-Kits, daher haben wir den Grundstein nur als IP-Kern für Tests gelegt, und die Feuertaufe erfolgte direkt nach der Herstellung von Prototypenplatinen.

Arbeiten Sie mit dem System auf dem Xilinx Zynq 7000-Chip




Die interne Struktur des SoC Xilinx Zynq 7000


Schritte zum Generieren von Startdateien für Xilinx Zynq

Das Flashen / Herunterladen von ausführbaren Dateien für Zynq unterscheidet sich vom üblichen Download für MPU. Die übliche Arbeit mit Cortex-A-Prozessoren ist das Laden von U-Boot, Kernel Linux und Rootfs. Und auf Zynq erscheint Bitstream, die Firmware-Datei für FPGAs. Der Bitstrom enthält eine Beschreibung der Hardwareblöcke auf dem FPGA und der internen Kommunikation mit dem Prozessor. Diese Datei wird beim Systemstart geladen. Auch auf der Linux-Seite gibt es einen Mechanismus, mit dem Sie den PL-Teil sofort während des Betriebs flashen können. Ein solches Gerät heißt xdevcfg ( ZYNQ FPGA Manager seit 2018.1 ).

PRI / BRI-Schnittstellen



Merkmale digitaler Netzwerke PRI / BRI

PRI (Primary Rate Interface) ist eine Standard-ISDN-Netzwerkschnittstelle, die die Disziplin des Verbindens von ISDN-Stationen mit Breitbandleitungen definiert, die lokale und zentrale Vermittlungsstellen oder Netzwerk-Switches verbinden.


Art des übertragenen Rahmens für PRI


Ansicht des übertragenen Rahmens für BRI


Die interne Struktur der PRI-Physik - IDT82P2288


Die interne Struktur der BRI-Physik - XHFC-4SU

Audio Codec TLV320AIC34


Der vierkanalige TLV320AIC34-Audiocodec mit geringem Stromverbrauch für tragbares Audio und Telefonie ist eine gute Lösung für die Verwendung in der analogen Telefonie.


Tlv320aic34 Einteiliger Audio-Codec enthält zwei solche Funktionsblöcke

Daten können über die I2S-Schnittstelle sowie über DSP, PCM und TDM übertragen werden.

I2S ist ein serieller Busschnittstellenstandard, der zum Anschließen digitaler Audiogeräte verwendet wird und 3 Leiter, die von einem aktiven zu einem passiven Gerät wechseln, sowie 4 entsprechende Signale elektrisch darstellt:

  1. Bittakt (BCLK).
  2. Taktsignalrahmen (nach Worten) Synchronisation (WCLK).
  3. Datensignal, das 2 zeitlich geteilte Kanäle (DIN / DOUT) senden oder empfangen kann.

Die Kanäle zum Empfangen und Senden von Daten sind unterteilt, dh es gibt einen separaten Kanal zum Empfangen von Daten und einen Kanal zum Senden. Der Controller empfängt die vom Audio-Codec übertragenen Daten, aber auch das Gegenteil ist möglich.


I2S-Frame, Funktionen der I2S-Schnittstelle

Nachdem wir alle Hardwarekomponenten ausgewählt hatten, lösten wir das Problem der Verbindung von Audio-Codec und Xilinx Zynq 7020.

Suchen Sie nach I2S-Kernen


Der wahrscheinlich schwierigste Moment bei der Arbeit mit dem Audiostream im Xilinx Zynq 7020 war, dass sich auf dem Prozessor dieses Systems im Grunde kein I2S-Bus auf dem Chip befindet, sodass ich den I2S-Kern finden musste. Diese Aufgabe wurde durch die Bedingung erschwert, dass der IP-Core frei sein sollte.

Wir haben uns für mehrere IP-Cores entschieden. Gefunden für Bare-Metal-Core I2S Digilent . Wir haben mehrere IP-Cores auf OpenCores gefunden und wahrscheinlich ist die beste Option für uns der IP-Core von Analog Devices . Sie produzieren IP-Cores für ihre Geräte, für die FPGA / FPGA-Interaktion.

Wir interessieren uns für den IP-Core namens AXI-I2S-ADI. Analog Devices selbst bewirbt diese IP-Cores für seine Hardwareplattformen.

Gesamtliste der Anwendungsfälle:

  1. Bare Metal - IP-Core für I2S (Digilent ZYBO Audio)
  2. opencores.org
  3. AXI-I2S-ADI-Controller (analoge Geräte)

AXI-I2S-ADI IP Core


Der IP-Core selbst sieht folgendermaßen aus: Er enthält die Zeilen bclk, wclk, din, dout. Es wird eine Verbindung zum DMA Xilinx Zynq 7000 hergestellt. In unserem Beispiel wird der DMA PS-Teil verwendet. Der gesamte Datenaustausch erfolgt über DMA. DMA kann eine eigenständige Einheit oder ein integraler Bestandteil von PS SoC sein.

Bei der Konfiguration dieses IP-Kernels ist es wichtig, nicht zu vergessen, die mclk-Master-Frequenz an tlv320aic34 selbst zu senden. Optional, wenn Sie das Debugging-Kit für tlv320aic34 verwenden - senden Sie eine externe Master-Frequenz.


Funktionsbaustein mit angeschlossenem axi-i2s-adi

Nach dem Konfigurationsvorgang bestand die Aufgabe darin, die Funktionalität unter Linux zu starten.

Starten und konfigurieren Sie den Gerätebaum für tlv320aic34


I2c konfigurieren (tlv320aic34 ist auf dieser Schnittstelle konfiguriert):

i2c0: i2c@e0004000 { ... tlv320aic3x: tlv320aic3x@18 { #sound-dai-cells = <0>; compatible = "ti,tlv320aic3x"; reg = <0x18>; gpio-reset = <&axi_gpio_0 0 0>; ai3x-gpio-func = <&axi_gpio_0 1 0>, /* AIC3X_GPIO1_FUNC_DISABLED */ <&axi_gpio_0 2 0>; /* AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT */ AVDD-supply = <&vmmc2>; DRVDD-supply = <&vmmc2>; IOVDD-supply = <&vmmc2>; DVDD-supply = <&vmmc2>; ai3x-micbias-vg = <1>; }; ... }; 

I2s konfigurieren (Audiodaten werden über diese Schnittstelle übertragen):

 i2s_clk: i2s_clk { #clock-cells = <0>; compatible = "fixed-clock"; clock-frequency = <11289600>; clock-output-names = "i2s_clk"; }; axi_i2s_adi_0: axi_i2s_adi@43C00000 { compatible = "adi,axi-i2s-1.00.a"; reg = <0x43C00000 0x1000>; xlnx,bclk-pol = <0x0>; xlnx,dma-type = <0x1>; xlnx,has-rx = <0x1>; xlnx,has-tx = <0x1>; xlnx,lrclk-pol = <0x0>; xlnx,num-ch = <0x1>; xlnx,s-axi-min-size = <0x000001FF>; xlnx,slot-width = <0x18>; }; &axi_i2s_adi_0 { #sound-dai-cells = <0>; compatible = "adi,axi-i2s-1.00.a"; clocks = <&clkc 15>, <&i2s_clk>; clock-names = "axi", "ref"; dmas = <&dmac_s 0 &dmac_s 1>; dma-names = "tx", "rx"; }; 

Einstellen der Soundkarte im Gerätebaum (Audiokarten):

  sound { compatible = "simple-audio-card"; simple-audio-card,name = "TLV320AIC34"; simple-audio-card,format = "i2s"; simple-audio-card,bitclock-master = <&dailink0_master>; simple-audio-card,frame-master = <&dailink0_master>; simple-audio-card,widgets = ... simple-audio-card,routing = ... dailink0_master: simple-audio-card,cpu { clocks = <&i2s_clk>; sound-dai = <&axi_i2s_adi_0>; }; simple-audio-card,codec { clocks = <&i2s_clk>; sound-dai = <&tlv320aic3x>; }; }; }; 

Nach all den Manipulationen zum Konfigurieren und Konfigurieren des Codecs im Gerätebaum unter Linux erschien die begehrte Audiokarte und wir konnten Musik hören (unser erster Musiktitel war Highway to Hell, AC / DC).

Hier ist, was wir dafür tun mussten:

  • Generierte die erforderliche Frequenz mit clk_wiz (Taktungsassistent)
  • Richtig konfiguriertes DTS für tlv320aic34
  • Unterstützung für tlv320aic3x-Treiber hinzugefügt
  • Audiopakete zu buildroot hinzugefügt, um Audiostreams abzuspielen (aplay, madplay usw.)

Bei der Entwicklung des Endgeräts standen wir vor der Aufgabe, 4 tlv320aic34-Mikroschaltungen anzuschließen. Der oben beschriebene tlv320aic34-Chip enthält 2 Blöcke zum Arbeiten mit dem Audiostream. Jeder Block verfügt über eine eigene i2c-Leitung zum Konfigurieren und Einrichten von Audioparametern. Ein Block kann jeweils nur vier Adressen haben. Es ist unmöglich, vier tlv320aic34-Mikroschaltungen an eine i2c-Schnittstelle anzuschließen. Sie müssen zwei i2c-Schnittstellen (8 unabhängige Audioblöcke) verwenden. Wenn Sie mclk, blck, wclk, din / dout einzeln starten, müssen Sie für jeden Block insgesamt 40 Signalleitungen hinzufügen, was aus Sicht der Schaltung für das von uns ausgewählte som-Modul unmöglich und irrational ist, da Sie zusätzlich zu diesen Signalen viele andere Leitungen und anschließen mussten Schnittstellen.

Aus diesem Grund haben wir beschlossen, die Audiokarte in den TDM-Modus zu schalten , in dem alle mclk-, bclk-, din- und dout-Leitungen kombiniert werden, wodurch sich die Gesamtzahl der Kommunikationsleitungen verringert. Diese Entscheidung wirkte sich auf den Betrieb von axi-i2s-adi aus, da der IP-Core selbst im Master-Modus arbeitete. Diese Änderung erlaubte es uns auch nicht, unseren IP-Core im TDM-Modus zu verwenden, und eine willensstarke Entscheidung, die Verwendung des ausgewählten IP-Core aufzugeben. Ich musste einen IP-Kernel schreiben, um den i2s-Verkehr abzuhören und an dma zu senden. Mit dieser Lösung konnten wir eine gemeinsame Schnittstelle für den Empfang von Daten erstellen, die nicht vom Kartentyp für die Aufzeichnung von Anrufen abhängen (analoge und digitale Karten).

Die anfängliche Architektur zum Empfangen des Audiostreams und seiner Verarbeitung über die i2s-Schnittstelle:



Die endgültige Architektur für den Empfang des Audiostreams und seine Verarbeitung über die i2s-Schnittstelle:



Architektur des Empfangs eines PRI-Streams und seiner Verarbeitung:



Architektur des Empfangs und der Verarbeitung von BRI-Streams:



Axi dma


Dies ist ein wichtiges Element des Datensynchronisationssystems für dma.


AXI DMA-Konfigurationsfenster in Xilinx Vivado

Auf dem Druckbildschirm wird der AXI DMA-Block selbst angezeigt. Es hat viele Parameter. Sie können dem Bus konfigurieren, wie viele Daten übertragen werden sollen. Daten können ausgerichtet oder in einem beliebigen Format sein. Eine detaillierte Beschreibung der Funktionsweise und Interaktion mit Axi Dma ist in der technischen Dokumentation beschrieben (von Version zu Version werden Ungenauigkeiten in der Beschreibung hinzugefügt und korrigiert sowie IP-Kernel verfeinert).

Überprüfen Sie die Datenübertragung über die Testoptionen AXI DMA und AXI DMA


Bei der Entwicklung des Treibers haben wir uns entschlossen, Open Source zu finden und an unsere Aufgabe anzupassen. Als Ergebnis haben wir die Quellen des Github-Projekts ezdma ausgewählt (Wortspiel, gelesen als easy dma).

Der nächste Schritt ist die Entwicklung eines Testtreibers. Dies war eine Vorbereitungsphase für den Moment, als ein IP-Core mit vorgefertigten Funktionen aus der FPGA-Entwicklungsabteilung zu uns kam (der beschriebene Entwicklungsprozess wurde von eingebetteten Programmierern gebildet). Vor diesem Moment haben wir uns entschlossen, AXI DMA und AXI DATA FIFO zu verwenden und einen Loopback durchzuführen, um zukünftige Fehler zu vermeiden. Wir haben das Senden und Empfangen von Daten geloopt und das Ergebnis unserer Arbeit und die Leistung unseres Fahrers überprüft. Wir haben die Funktionalität ein wenig angepasst, sie auf der Interaktionsoberfläche an unsere Wünsche angepasst und erneut die Funktionsfähigkeit des Treibers und das ausgewählte Interaktionsprinzip überprüft.


Look-Back-Block-Design, der erste Weg, um AXI DMA zu testen

Ein Beispiel für eine Beschreibung von DMA und ezdma in einem Gerätebaum:

 / { amba_pl: amba_pl { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; ranges ; axi_dma_1: axi_dma { #dma-cells = <1>; compatible = "xlnx,axi-dma-1.00.a"; reg = <0x40400000 0x10000>; clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk"; clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>, <&clkc 15>; interrupt-parent = <&intc>; interrupts = <0 29 4 0 30 4>; xlnx,addrwidth = <0x20>; xlnx,include-sg; dma-channel@40400000 { compatible = "xlnx,axi-dma-mm2s-channel"; dma-channels = <0x1>; interrupts = <0 29 4>; xlnx,datawidth = <0x20>; xlnx,device-id = <0x0>; xlnx,include-dre ; }; dma-channel@40400030 { compatible = "xlnx,axi-dma-s2mm-channel"; dma-channels = <0x1>; interrupts = <0 30 4>; xlnx,datawidth = <0x20>; xlnx,device-id = <0x0>; xlnx,include-dre ; }; }; ezdma0 { compatible = "ezdma"; dmas = <&axi_dma_1 0 &axi_dma_1 1>; dma-names = "loop_tx", "loop_rx"; // used when obtaining reference to above DMA core using dma_request_slave_channel() ezdma,dirs = <2 1>; // direction of DMA channel: 1 = RX (dev->cpu), 2 = TX (cpu->dev) }; ... }; }; 

Mit dem Tool Device Tree Generator können Sie problemlos dts / dtsi-Dateien erstellen.

Der zweite Schritt in unserem Entwicklungsprozess ist die Erstellung eines Test-IP-Kernels zur Überprüfung der Treiberleistung. Nur dieses Mal sind die Daten aussagekräftig, und die Übertragung über AXIS an AXI_DMA (wie in der endgültigen Version des IP-Kernels).


Workflow der AXIS-Schnittstelle

Wir implementieren zwei Varianten von IP-Kerneln zur Datengenerierung, die erste getestete Version wird über verilog implementiert, die zweite - auf HLS (in diesem Zusammenhang erschien HLS unter dem Motto „stylisch-mode-jugend“).

Der Verilog-Datengenerator (und im Allgemeinen in den Sprachen der HDL-Familie - Verilog, VHDL usw.) ist eine Standardlösung bei der Entwicklung von IP-Kernen dieses Typs. Hier sind einige Codefragmente für den Zwischen-IP-Kernel:

 module GenCnt ( …. assign HandsHake = m_axis_din_tready & m_axis_dout_tvalid; always @(posedge Clk) begin if (Rst) begin smCnt <= sIDLE; end else begin case (smCnt) sIDLE: begin smCnt <= sDATA; end sDATA: begin if (Cnt == cTopCnt - 1) begin smCnt <= sLAST; end end ... endmodule 

Eine detailliertere Beschreibung ist nicht erforderlich, da dies eine typische Aufgabe eines FPGA-Designers ist.

Ein interessanteres "Biest" ist hier HLS. Vivado HLS (High Level Synthesis) ist die neue Xilinx CAD-Software zum Erstellen digitaler Geräte mit Hochsprachen wie OpenCL, C oder C ++.

C / C ++ sind die Hauptsprachen für einen Embedded-Software-Ingenieur. Daher ist die Lösung eines Problems mit diesen Sprachen im Hinblick auf die Implementierung und vergleichende Analyse für zukünftige Projekte interessanter.

Hier sind zwei kleine Beispiele für die Arbeit mit HLS. Das erste Beispiel ist ein Datengenerator für AXI_DMA, das zweite Beispiel ist der Datenaustausch zwischen dem Prozessorteil und der programmierbaren Logik über die Schnittstelle s_axilite.

Der Datenaustausch über die s_axilite-Schnittstelle (das zweite Beispiel) wurde so implementiert, dass jederzeit in procfs der geladene Bitstrom subtrahiert werden konnte und die Korrektheit der Arbeit durch Versionierung für den PL-Teil von SoC verfolgt werden konnte. Hier erscheint ein sehr interessanter Punkt bei s_axilite: Vivado HLS generiert einen Treiber für Linux (der Treiber wiederum haben wir angepasst, um procfs zu verarbeiten, um die Vererbung des Schreibens zu bewahren). Ein Beispiel für den generierten Code für Linux finden Sie unten (der Pfad zur Quelllösung1 / impl / ip / drivers / name_xxx / src /).


Stufen der HLS-Synthese und der RTL-Codegenerierung

HLS-Datengenerator zur Überprüfung des Betriebs mit AXI_DMA:

 #include <ap_axi_sdata.h> #include <hls_stream.h> #define SIZE_STREAM 1024 struct axis { int tdata; bool tlast; }; void data_generation(axis outStream[SIZE_STREAM]) { #pragma HLS INTERFACE axis port=outStream int i = 0; do{ outStream[i].tdata = i; outStream[i].tlast = (i == (SIZE_STREAM - 1)) ? 1 : 0; i++; }while( i < SIZE_STREAM); } 

Ein Beispiel für die Versionierung und den Typ der Schnittstellenkarte:

 #include <stdio.h> void info( int &aVersion, int &bSubVersion, int &cTypeBoard, int version, int subVersion, int typeBoard ){ #pragma HLS INTERFACE s_axilite port=aVersion #pragma HLS INTERFACE s_axilite port=bSubVersion #pragma HLS INTERFACE s_axilite port=cTypeBoard #pragma HLS INTERFACE ap_ctrl_none port=return aVersion = version; bSubVersion = subVersion; cTypeBoard = typeBoard; } 

Wie Sie bemerkt haben, ist es für die Entwicklung auf hls sehr wichtig, die Arbeit und Anwendung verschiedener Pragmas (HLS-Pragma) zu verstehen, da der Synthesevorgang direkt mit Pragmas verbunden ist.

Generierter Treiber für s_axilite:

 // ============================================================== // File generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC // Version: 2016.4 // Copyright (C) 1986-2016 Xilinx, Inc. All Rights Reserved. // // ============================================================== #ifdef __linux__ /***************************** Include Files *********************************/ #include "xinfo.h" /***************** Macros (Inline Functions) Definitions *********************/ #define MAX_UIO_PATH_SIZE 256 #define MAX_UIO_NAME_SIZE 64 #define MAX_UIO_MAPS 5 #define UIO_INVALID_ADDR 0 /**************************** Type Definitions ******************************/ typedef struct { u32 addr; u32 size; } XInfo_uio_map; typedef struct { int uio_fd; int uio_num; char name[ MAX_UIO_NAME_SIZE ]; char version[ MAX_UIO_NAME_SIZE ]; XInfo_uio_map maps[ MAX_UIO_MAPS ]; } XInfo_uio_info; /***************** Variable Definitions **************************************/ static XInfo_uio_info uio_info; /************************** Function Implementation *************************/ static int line_from_file(char* filename, char* linebuf) { char* s; int i; FILE* fp = fopen(filename, "r"); if (!fp) return -1; s = fgets(linebuf, MAX_UIO_NAME_SIZE, fp); fclose(fp); if (!s) return -2; for (i=0; (*s)&&(i<MAX_UIO_NAME_SIZE); i++) { if (*s == '\n') *s = 0; s++; } return 0; } static int uio_info_read_name(XInfo_uio_info* info) { char file[ MAX_UIO_PATH_SIZE ]; sprintf(file, "/sys/class/uio/uio%d/name", info->uio_num); return line_from_file(file, info->name); } static int uio_info_read_version(XInfo_uio_info* info) { char file[ MAX_UIO_PATH_SIZE ]; sprintf(file, "/sys/class/uio/uio%d/version", info->uio_num); return line_from_file(file, info->version); } static int uio_info_read_map_addr(XInfo_uio_info* info, int n) { int ret; char file[ MAX_UIO_PATH_SIZE ]; info->maps[n].addr = UIO_INVALID_ADDR; sprintf(file, "/sys/class/uio/uio%d/maps/map%d/addr", info->uio_num, n); FILE* fp = fopen(file, "r"); if (!fp) return -1; ret = fscanf(fp, "0x%x", &info->maps[n].addr); fclose(fp); if (ret < 0) return -2; return 0; } static int uio_info_read_map_size(XInfo_uio_info* info, int n) { int ret; char file[ MAX_UIO_PATH_SIZE ]; sprintf(file, "/sys/class/uio/uio%d/maps/map%d/size", info->uio_num, n); FILE* fp = fopen(file, "r"); if (!fp) return -1; ret = fscanf(fp, "0x%x", &info->maps[n].size); fclose(fp); if (ret < 0) return -2; return 0; } int XInfo_Initialize(XInfo *InstancePtr, const char* InstanceName) { XInfo_uio_info *InfoPtr = &uio_info; struct dirent **namelist; int i, n; char* s; char file[ MAX_UIO_PATH_SIZE ]; char name[ MAX_UIO_NAME_SIZE ]; int flag = 0; assert(InstancePtr != NULL); n = scandir("/sys/class/uio", &namelist, 0, alphasort); if (n < 0) return XST_DEVICE_NOT_FOUND; for (i = 0; i < n; i++) { strcpy(file, "/sys/class/uio/"); strcat(file, namelist[i]->d_name); strcat(file, "/name"); if ((line_from_file(file, name) == 0) && (strcmp(name, InstanceName) == 0)) { flag = 1; s = namelist[i]->d_name; s += 3; // "uio" InfoPtr->uio_num = atoi(s); break; } } if (flag == 0) return XST_DEVICE_NOT_FOUND; uio_info_read_name(InfoPtr); uio_info_read_version(InfoPtr); for (n = 0; n < MAX_UIO_MAPS; ++n) { uio_info_read_map_addr(InfoPtr, n); uio_info_read_map_size(InfoPtr, n); } sprintf(file, "/dev/uio%d", InfoPtr->uio_num); if ((InfoPtr->uio_fd = open(file, O_RDWR)) < 0) { return XST_OPEN_DEVICE_FAILED; } // NOTE: slave interface 'Axilites' should be mapped to uioX/map0 InstancePtr->Axilites_BaseAddress = (u32)mmap(NULL, InfoPtr->maps[0].size, PROT_READ|PROT_WRITE, MAP_SHARED, InfoPtr->uio_fd, 0 * getpagesize()); assert(InstancePtr->Axilites_BaseAddress); InstancePtr->IsReady = XIL_COMPONENT_IS_READY; return XST_SUCCESS; } int XInfo_Release(XInfo *InstancePtr) { XInfo_uio_info *InfoPtr = &uio_info; assert(InstancePtr != NULL); assert(InstancePtr->IsReady == XIL_COMPONENT_IS_READY); munmap((void*)InstancePtr->Axilites_BaseAddress, InfoPtr->maps[0].size); close(InfoPtr->uio_fd); return XST_SUCCESS; } #endif 

Eine wichtige Datei, die Ihnen den Speicherort von Variablen (Registern) im Adressraum angibt, ist die Datei x # your_name # _hw.h. Sie können die Richtigkeit des geschriebenen IP-Kernels jederzeit mit dem devmem-Tool überprüfen.

Der Inhalt dieser Datei:

 // ============================================================== // File generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC // Version: 2016.4 // Copyright (C) 1986-2016 Xilinx, Inc. All Rights Reserved. // // ============================================================== // AXILiteS // 0x00 : reserved // 0x04 : reserved // 0x08 : reserved // 0x0c : reserved // 0x10 : Data signal of aVersion // bit 31~0 - aVersion[31:0] (Read) // 0x14 : Control signal of aVersion // bit 0 - aVersion_ap_vld (Read/COR) // others - reserved // 0x18 : Data signal of bSubVersion // bit 31~0 - bSubVersion[31:0] (Read) // 0x1c : Control signal of bSubVersion // bit 0 - bSubVersion_ap_vld (Read/COR) // others - reserved // 0x20 : Data signal of cTypeBoard // bit 31~0 - cTypeBoard[31:0] (Read) // 0x24 : Control signal of cTypeBoard // bit 0 - cTypeBoard_ap_vld (Read/COR) // others - reserved // (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake) #define XINFO_AXILITES_ADDR_AVERSION_DATA 0x10 #define XINFO_AXILITES_BITS_AVERSION_DATA 32 #define XINFO_AXILITES_ADDR_AVERSION_CTRL 0x14 #define XINFO_AXILITES_ADDR_BSUBVERSION_DATA 0x18 #define XINFO_AXILITES_BITS_BSUBVERSION_DATA 32 #define XINFO_AXILITES_ADDR_BSUBVERSION_CTRL 0x1c #define XINFO_AXILITES_ADDR_CTYPEBOARD_DATA 0x20 #define XINFO_AXILITES_BITS_CTYPEBOARD_DATA 32 #define XINFO_AXILITES_ADDR_CTYPEBOARD_CTRL 0x24 

Diese Datei beschreibt die Adressen der Register, die Register entsprechen der Position der Argumente in der Funktion. Nach der Synthese des Projekts können Sie sehen, wie das erstellte Projekt in Zyklen ausgeführt wird.


Projekt Beat Beispiel

Die Arbeit mit hls hat gezeigt, dass dieses Tool zur schnellen Lösung von Aufgaben geeignet ist, insbesondere zur Lösung mathematischer Probleme der Computer Vision, die in C ++ oder C leicht beschrieben werden können, sowie zur Erstellung kleiner IP-Kernel für Interaktionen und Austausch Informationen mit Standard-FPGA-Schnittstellen.

Gleichzeitig ist HLS nicht für die Implementierung bestimmter Hardwareschnittstellen geeignet, beispielsweise in unserem Fall I2S, und der generierte RTL-Code nimmt auf dem FPGA mehr Platz ein als in Standard-HDL-Sprachen geschrieben.

Der letzte Schritt beim Treiber-Test ist die Entwicklung eines I2S-Verkehrsgenerators. Dieser IP-Core wiederholt die Funktionalität früherer IP-Kernel, mit der Ausnahme, dass er inkrementelle Daten (Datenverkehr) generiert, die echten I2S-Daten im TDM-Modus entsprechen.


Blockdesign für zukünftige benutzerdefinierte I2S-Kerntests und I2S-Verkehrsgenerator

Als Ergebnis haben wir die Ergebnisse von hls, axi dma und s_axilite erhalten und die Leistung unserer Software und Treiber überprüft.

Schlussfolgerungen


Wir haben es geschafft, die notwendigen Arten von Schnittstellenkarten sowie IP-Kernel für tdm, pri, bri zu entwickeln. Wir haben den aktuellen Ansatz für die Entwicklung solcher Geräte erheblich verbessert und eine umfassende Lösung geschaffen, die mit ähnlichen Schnittstellenkarten von Asterick , Patton und anderen konkurrieren kann. Der Vorteil unserer Lösung besteht darin, dass der Entwickler für die Datenübertragung keine Zwischenverbindung zwischen PC und PCI benötigt, sondern die empfangenen Informationen direkt über Ethernet übertragen kann.

Source: https://habr.com/ru/post/de415435/


All Articles