Forum: FPGA, VHDL & Co. Massenspeicher an Zynq als USB-Host


von Julian B. (Firma: Hochschule Reutlingen) (julian-bauer)


Lesenswert?

Hallo,

ich würde gerne meine aktuelle Bare-Metal Applikation mit DMA- und 
Ethernettransfer um einen Massenspeicher erweitern.
Dazu sind erst einmal folgende Dinge notwendig:
-File-System um auf den Massenspeicher zuzugreifen
-Treiber für USB und evtl. SD
-einen Stack für den USB Host.

Die Ansprüche sind erst einmal nicht sehr hoch. Bei der Datenrate wären 
ca. 100 MB/s wünschenswert, notwendig sind jedoch nur zwischen 10-50 
MB/s.
Auf dem MicroZed Board werden Daten generiert, welche aktuell über 
Ethernet versendet werden, jedoch zusätzlich/alternativ auf eine externe 
Festplatte geschrieben werden sollen.

Vor Linux scheue ich mich momentan deshalb, da ich eine recht 
performante Anwendung mit eigenem TCP/IP Stack auf Basis einer 
Bare-metal Anwendung erstellt habe. Es besteht die Möglichkeit, ein 
Filesystem + USB Treiber mittels eines Linux auf einem der beiden Kerne 
zum Laufen zu bringen und auf dem zweiten Kern die Bare-Metal Anwendung 
laufen zu lassen - finde ich jedoch etwas umständlich.

nun habe ich bereits einen bare-metal driver gefunden, dieser 
initialisert das Processing System des Zynq jedoch ausschließlich als 
DEVICE, nicht als HOST:
1
  /* Set the USB mode register to configure DEVICE mode.
2
   *
3
   * XUSBPS_MODE_SLOM_MASK note:
4
   *   Disable Setup Lockout. Setup Lockout is not required as we
5
   *   will be using the tripwire mechanism when handling setup
6
   *   packets.
7
   */
8
  ModeValue = XUSBPS_MODE_CM_DEVICE_MASK;
9
10
  XUsbPs_WriteReg(InstancePtr->Config.BaseAddress,
11
        XUSBPS_MODE_OFFSET, ModeValue);
12
13
  XUsbPs_SetBits(InstancePtr, XUSBPS_OTGCSR_OFFSET,
14
        XUSBPS_OTGSC_OT_MASK);
ModeValue = 0 setzt den USB Mode als DEVICE MODE
ModeValue = 1 setzt den USB Mode als HOST MODE, nur leider ist schon das 
im Board Support Package fest codiert, eine Konfiguration als DEVICE 
wird nicht vorgesehen.

es wird jedoch immer wieder angesprochen, dass auch der Host Mode 
unterstützt werden würde:
1
 * - Supports Low Speed USB 1.1 (1.5Mbps), Full Speed USB 1.1 (12Mbps), and
2
 *   High Speed USB 2.0 (480Mbps) data speeds
3
 * - Supports Device, Host and OTG operational modes
4
 * - ULPI transceiver interface for USB 2.0 operation
5
 * - Integrated USB Full and Low speed serial transceiver interfaces for lowest
6
 *   cost connections

1
/** @name Operational Register offsets.
2
 * Register comments are tagged with "H:" and "D:" for Host and Device modes,
3
 * respectively.
4
 * Tags are only present for registers that have a different meaning DEVICE and
5
 * HOST modes. Most registers are only valid for either DEVICE or HOST mode.
6
 * Those registers don't have tags.

ich denke jedoch, dass es nicht so ohne weiteres möglich ist? Leider 
gibt es absolut nichts gescheites im Netz zu finden!
Alternativ würde auch eine käuflich erwerbbare Version in Frage kommen, 
falls hier jemand mehr weiß?

Wäre schön wenn mir jemand einen möglichen Lösungweg nennen könnte, da 
ich aktuell nicht weiter komme.

Vielen Dank!
Grüße
Julian

: Bearbeitet durch User
von Strubi (Gast)


Lesenswert?

Moin Julian,


Julian B. schrieb:
> Die Ansprüche sind erst einmal nicht sehr hoch. Bei der Datenrate wären
> ca. 100 MB/s wünschenswert, notwendig sind jedoch nur zwischen 10-50
> MB/s.

Auch bei den 50 MByte/s ist Sense mit USB 2.0, ne. Da müsstest du schon 
in Richtung USB3.0 gehen oder gar SATA in Betracht ziehen. Oder Daten 
komprimieren...


> Filesystem + USB Treiber mittels eines Linux auf einem der beiden Kerne
> zum Laufen zu bringen und auf dem zweiten Kern die Bare-Metal Anwendung
> laufen zu lassen - finde ich jedoch etwas umständlich.

Gerade die Ketzerei hätte ich jetzt am ehesten noch begangen.
Denn USB und effiziente DMA in Bare Metal hochzuziehen und ans Limit zu 
trimmen ist gehörig komplex, unter dem Aspekt dass auch noch ATA-Layer 
und Filesystem zu implementieren sind. Da gibts schon eine Menge Code, 
aber bis das alles so zusammenspielt...

Aber auch die Linux-Seite würde noch keine Garantie für deine 
Durchsatzanforderungen bieten. Ich bin nicht up to date, wie gut Xilinx 
die Kernelseite abgedeckt hat, mein letzter Statuscheck war noch in der 
"Steinzeit".

von Julian B. (Firma: Hochschule Reutlingen) (julian-bauer)


Lesenswert?

Hi Strubi :)

ok 50MB ist auch das maximum was übertragen werden soll, mit Abstrichen 
sind 10-25MB immernoch sehr toll. Darunter allerdings macht es nur noch 
wenig Sinn.

Wäre es auch möglich einen bestehenden Stack auf den Zynq zu portieren? 
Und dies auch ohne Filesystem, oder bedingt die USB-Host Funktion 
unbedingt ein Filesystem?

Gibts irgend etwas um "schnell" mal eine einfache Linux Applikation mit 
Filesystem und USB-Host-Funktionalität zum Laufen zu bekommen, ohne dass 
ich hier Wochen umsonst verschwende und schließlich das Thema doch 
verwerfen muss?

von Strubi (Gast)


Lesenswert?

Hi Julian,

> Wäre es auch möglich einen bestehenden Stack auf den Zynq zu portieren?
> Und dies auch ohne Filesystem, oder bedingt die USB-Host Funktion
> unbedingt ein Filesystem?
>

Nee, wenn du nur Sektoren/Blöcke schreiben willst, reicht die 
Implementierung einiger USB-SCSI-Kommandos, wenn das USB I/O für deine 
HW bereits abgehakt ist. Die Header dazu findest du im Linux-Source 
(siehe struct sg_io_hdr_t, wenn ich nicht falsch liege)
Es gibt sonst auch ganz bekloppte Leute, die spiffs (spi flash 
filesystem) für andre Medien missbrauchen. Geht sehr gut "bare metal".

> Gibts irgend etwas um "schnell" mal eine einfache Linux Applikation mit
> Filesystem und USB-Host-Funktionalität zum Laufen zu bekommen, ohne dass
> ich hier Wochen umsonst verschwende und schließlich das Thema doch
> verwerfen muss?

Wenn "mal schnell", dann wohl so: Linux mit USB-Host-Unterstützung auf 
den Zynq braten, USB-Stick einstecken und 'dmesg' checken.
Dann bleibt das Problem nur noch, wie du effizient (typischerweise auch 
per  memory to memory dma) die Daten zwischen den Cores austauschst. 
Weiss aber nicht, ob Xilinx für sowas schon Kerneltreiber-Templates 
bereitgestellt hat.

von Julian B. (Firma: Hochschule Reutlingen) (julian-bauer)


Lesenswert?

Strubi schrieb:
> Wenn "mal schnell", dann wohl so: Linux mit USB-Host-Unterstützung auf
> den Zynq braten, USB-Stick einstecken und 'dmesg' checken.
> Dann bleibt das Problem nur noch, wie du effizient (typischerweise auch
> per  memory to memory dma) die Daten zwischen den Cores austauschst.
> Weiss aber nicht, ob Xilinx für sowas schon Kerneltreiber-Templates
> bereitgestellt hat.

Danke mal wieder für deine Hilfe, Strubi.

Also ich habe jetzt die XAPP1078 zum laufen bekommen, viel mehr selbst 
nachgebildet, ich kann also ein neues VHDL Design abändern, Petalinux 
abändern und den Code abändern und es läuft soweit alles.

Gibt aber bis ich zu USB komme noch einige Baustellen, die ich angehen 
muss, bevor das eigentliche Thema USB hoch kommt. Nun läuft Linux unter 
Core0 und baremetal unter core1.
linux wird gebootet. die baremetal-app liegt auf addr 0x30000000 im RAM, 
wird diese Adresse ins OCM bei 0xFFFFFFF0 geschrieben, wird die app 
gestartet.

Aufgrund des Linux OS kann nur noch mit global timer und private 
interrupts gearbeitet werden. Core1_nIRQ wird in der demo app verwendet. 
nun wollte ich Core1_nFIQ zusätzlich implementieren, bekomme ihn aber 
nicht verbunden/ausgelöst, hier meine Initialisierung:
1
static int SetupIntrSystem(  INTC *IntcInstancePtr,
2
              XPlIrq *PeriphInstancePtr,
3
              u16 IntrId,
4
              XPlIrq *PeriphInstancePtr2,
5
              u16 IntrId2)
6
{
7
8
  int Status;
9
10
11
  XScuGic_Config *IntcConfig;
12
13
  /*
14
   * Initialize the interrupt controller driver so that it is ready to
15
   * use.
16
   */
17
  IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
18
  if (NULL == IntcConfig) {
19
    return XST_FAILURE;
20
  }
21
22
  Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
23
          IntcConfig->CpuBaseAddress);
24
  if (Status != XST_SUCCESS) {
25
    return XST_FAILURE;
26
  }
27
28
  /*
29
   * Connect the interrupt handler that will be called when an
30
   * interrupt occurs for the device.
31
   */
32
  Status = XScuGic_Connect(IntcInstancePtr, IntrId,
33
         (Xil_ExceptionHandler)PlIntrHandler,
34
         PeriphInstancePtr);
35
  if (Status != XST_SUCCESS) {
36
    xil_printf("ERROR1");
37
    return Status;
38
  }
39
40
  Status = XScuGic_Connect(IntcInstancePtr, IntrId2,
41
         (Xil_ExceptionHandler)PlIntrHandler2,
42
         PeriphInstancePtr2);
43
  if (Status != XST_SUCCESS) {
44
    xil_printf("ERROR2");
45
    return Status;
46
  }
47
48
  /*
49
   * Enable the interrupt for the PL device.
50
   */
51
  XScuGic_Enable(IntcInstancePtr, IntrId);
52
  XScuGic_Enable(IntcInstancePtr, IntrId2);
53
54
  /*
55
   * Initialize the  exception table
56
   */
57
  Xil_ExceptionInit();
58
59
  /*
60
   * Register the interrupt controller handler with the exception table
61
   */
62
  Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
63
       (Xil_ExceptionHandler)XScuGic_InterruptHandler,
64
       IntcInstancePtr);
65
66
  /*
67
   * Register the interrupt controller handler with the exception table
68
   */
69
  Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_FIQ_INT,
70
       (Xil_ExceptionHandler)XScuGic_InterruptHandler,
71
       IntcInstancePtr);
72
73
  /*
74
   * Enable non-critical exceptions
75
   */
76
  //Xil_ExceptionEnable();
77
  Xil_ExceptionEnableMask(XIL_EXCEPTION_ALL);
78
79
80
  return XST_SUCCESS;
81
}

das modul IRQ_gen0 (0x78600000) ist verbunden mit core1_nIRQ,
das modul IRQ_gen1 (0x78700000) ist verbunden mit core1_nFIQ.

Wird an Addr 0x78600000 löst das modul einen interrupt aus, der wieder 
quitiert werden muss, ebenso IRQ_gen1.

Funktioniert aber nicht. Ich muss beide Interrupts auslösen, dann wird 
nur der nIRQ ausgelöst und quitiert, nFIQ bleibt gesetzt. Wenn nFIQ aber 
nicht gesetzt ist, löst auch nIRQ nicht aus.

Woran liegt das???

von Julian B. (Firma: Hochschule Reutlingen) (julian-bauer)


Angehängte Dateien:

Lesenswert?

ich hab nun die interrupt - thematik mal auf eis gelegt, war nur ein 
Randproblem.
nun starte ich petalinux auf core0 und baremetal auf core1. funktioniert 
auch. wenn ich aber auf core1 meine lwip anwendung laufen lasse bekomme 
ich keine tcp verbindung hin! :(

wie im anhang zu sehen ist, kommuniziert das fpga (192.168.202.10) mit 
dem host (192.168.202.86), dieser meldet sich dann zwar auf die erste 
anfrage zurück, danach sollte jedoch auch eine verbindung ausgelöst 
werden. daran hängt es aber gerade eben! der "connected_callback" wird 
in lwip nicht ausgelöst, obwohl ich "tcp_connect()" ausführe und die 
beiden timer (fast & slow) auch zur richtigen zeit kommen.

woran kann das liegen?
hat der zweite kern keinen zugriff mehr auf die hardware durch das 
petalinux? ist das aus xapp1078 modifizierte BSP auch hier modifiziert 
worden?(offiziell wurde die uart output() funktion verändert und der 
private timer anders initialisiert.
dies dürfte jedoch keinen effekt auf lwip oder ethernet haben?

https://gist.github.com/imrickysu/8c9bb9a0a0c40f24d718

bitte noch einmal um hilfe, denn ansonsten scheint alles wunderbar zu 
funktionieren!

von Strubi (Gast)


Lesenswert?

Hi Julian,

sorry, das muss ich passen. Sieht fast so aus, als ob Xilinx es immer 
noch bisschen auf ihre eigene Weise machen, wegen solcher Scherereien 
flog damals bei mir der Zynq in die Ecke.
Die Crux bei Linux ist halt schnell, dass es sehr komplex wird, wenn die 
board-supply-Packages nicht durchdacht sind/das machen sollen, was der 
User will. Hast du mal versucht, am Device-Tree (*.dts) rumzudrehen? Das 
wäre typischerweise die eleganteste Methode, Resourcen zu kontrollieren. 
Sollt ja ein aktuelles Kernel sein...
Ansonsten hilft vermutlich nur HW-Debugging, aber fang bloss nicht mit 
OpenOCD an, das Linux-Kernel zu debuggen (Albtraum garantiert).

Gruss,

- Strubi

von Julian B. (Firma: Hochschule Reutlingen) (julian-bauer)


Lesenswert?

Hi Strubi,

du bist auch der einzige der hier aktiv ist, oder? :P
vielen DAnk für deine antworten!

ich habe den ethernet controller in .dts komplett entfernt, dann wird er 
nicht mehr initialisiert und antwortet bei umkonfiguration auch nicht 
mehr darauf. linux scheint also die verbindung zum eth0 verloren zu 
haben.

dennoch scheine ich keinen zugang zum hardwaretreiber zu haben. die BSP 
sind auch leider zu undurchsichtig auf treiberebene, ich kann leider 
nicht erkennen wo der haken ist.
ich bekomme zum beispiel den error-callback nachdem die timeouts für das 
senden der SYN-segmente erreicht ist. prinzipiell funktioniert also ja 
zumindest etwas. das SYN wird aber physikalisch nie übertragen, generell 
keine TCP protokolle. am anfang lediglich ARP protokolle.
komme so leider nicht weiter und weiß nicht in welche richtung ich nun 
gehen soll :/

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.