Zde můžete vidět rozdíly mezi vybranou verzí a aktuální verzí dané stránky.
Obě strany předchozí revize Předchozí verze Následující verze | Předchozí verze | ||
2017:microblaze-lwip [2018/01/14 14:49] Tomáš Svoboda |
2017:microblaze-lwip [2018/01/14 20:43] (aktuální) Tomáš Svoboda |
||
---|---|---|---|
Řádek 12: | Řádek 12: | ||
====== Úvod ====== | ====== Úvod ====== | ||
- | Cílem tohoto projektu je implementovat lwIP stack na 32bitový soft procesor Microblaze. lwIP stack je volně dostupný TCP/IP stack, který v sobě zahrnuje podporu mnoha protokolů - IP (jak IPv4, tak IPv6), TCP, UDP, apod., tak i klientů jako DHCP či DNS, volitelně pak HTTP či TFTP server. V rámci tohoto projektu je pozornost zaměřena především na protokol UDP. Samotný lwIP stack vyžaduje minimální nároky, mj. je dostupná i light verze, velká část potřebné paměti je dána velikostí odesílacího, resp. přijímacího bufferu. | + | Cílem tohoto projektu je implementovat lwIP stack na 32bitový soft procesor Microblaze. lwIP stack je volně dostupný TCP/IP stack, který v sobě zahrnuje podporu mnoha protokolů - IP (jak IPv4, tak IPv6), TCP, UDP, apod., tak i klientů jako DHCP či DNS, volitelně pak HTTP či TFTP server. V rámci tohoto projektu je pozornost zaměřena především na protokol UDP. Samotný lwIP stack vyžaduje minimální nároky, mj. je dostupná i light verze, otázka potřebné paměti je dána velikostí odesílacího, resp. přijímacího bufferu. |
- | Microblaze je 32bitový soft procesors RISC instrukční sadou, dostupný jako IP jádro pro FPGA Xilinx (vč. rodiny Zynq, která má i hardwarový ARM procesor). Je vytvořen za použití prostředků samotné struktury FPGA. Z hlediska konfigurace jsou možnosti poměrně bohaté - velikosti instrukční a datové cache, lokální paměť, násobičky, děličky, breakpointy, apod. Pro potřeby tohoto projektu není většina bloků využita - především pro úsporu samotných FPGA prostředků. | + | [[https://www.xilinx.com/products/design-tools/microblaze.html|Microblaze]] je 32bitový soft procesors RISC instrukční sadou, dostupný jako IP jádro pro FPGA Xilinx (vč. rodiny Zynq, která má i hardwarový ARM procesor). Je vytvořen za použití prostředků samotné struktury FPGA. Z hlediska konfigurace jsou možnosti poměrně bohaté - velikosti instrukční a datové cache, lokální paměť, násobičky, děličky, breakpointy, apod. Pro potřeby tohoto projektu není většina bloků využita - především pro úsporu samotných FPGA prostředků. |
- | V rámci řešení se předpokládá odesílání dat z paměti typu BRAM do počítače skrze protokol UDP. V počítači pak otestovat příjem pomocí programu Wireshark nebo zápis do *.txt/*.csv souboru za využití skriptovacího jazyka Python. Data uložená v paměti BRAM budou představovat fiktivní data, která budou v pozdějším využití (mimo tento projekt) nahrazena samotnými vzorky získanými z A/D převodníku. | + | V rámci řešení se předpokládá odesílání dat z paměti typu [[https://www.xilinx.com/products/intellectual-property/block_ram.html|BRAM]] do počítače skrze protokol UDP. V počítači pak otestovat příjem pomocí programu Wireshark nebo zápis do *.txt/*.csv souboru za využití skriptovacího jazyka Python. Data uložená v paměti BRAM budou představovat fiktivní data, která budou v pozdějším využití (mimo tento projekt) nahrazena skutečnými vzorky získanými z A/D převodníku. |
V rámci řešení je tedy nutno: | V rámci řešení je tedy nutno: | ||
Řádek 44: | Řádek 44: | ||
| | ||
====== Realizace ====== | ====== Realizace ====== | ||
- | Na obrázku 2 je znázorněna realizace. Základ tvoří samotná vývojová deska osazená obvodem FPGA. Na obvodu ja realizován samotný procesor, Bloková RAM (BRAM), patřičné kontroly pro přístup k paměti a MAC jádro (Medium Access Controller). V rámci procesoru jsou pak mj. využity především knihovny pro samotný lwIP stack, SPI komunikaci a UART, případně obsluha GPIO. Z externí obvodů dostupných na desce je využita DDR paměť (vyžaduje lwIP stack), převodník USB/UART - využito pro ladící výstupy, obvod fyzické vrstvy Ethernetu (rozhraní RGMII) a Flash paměť. Ve Flash paměti je uložena jedinečná MAC adresy v OTP registru, paměť používá SPI rozhraní. Posledním článkem je počítač - zde je spuštěn Python skript, který přijímá jednotlivé UDP datagramny, separuje jednotlivé vzorky a ukládá je do souboru typu CSV (data oddělená čárkou). | + | Na obrázku 2 je znázorněna realizace. Základ tvoří samotná vývojová deska osazená obvodem FPGA. Na obvodu ja realizován samotný procesor, Bloková RAM (BRAM), patřičné bloky pro přístup k paměti a MAC jádro (Medium Access Controller). V rámci procesoru jsou pak mj. využity především knihovny pro samotný lwIP stack, SPI komunikaci a UART, případně obsluha GPIO. Z externí obvodů dostupných na desce je využita DDR paměť (vyžaduje lwIP stack), převodník USB/UART - využito pro ladící výstupy, obvod fyzické vrstvy Ethernetu (rozhraní RGMII) a Flash paměť. Ve Flash paměti je uložena jedinečná MAC adresy v OTP registru, paměť používá SPI rozhraní. Posledním článkem je počítač - zde je spuštěn Python skript, který přijímá jednotlivé UDP datagramny, separuje jednotlivé vzorky a ukládá je do souboru typu CSV (data oddělená čárkou). |
{{ :2017:lwip_stack:impelemntace_mpoa(1).png?700 |}} | {{ :2017:lwip_stack:impelemntace_mpoa(1).png?700 |}} | ||
**Obr.2** Blokové znázornění realizace | **Obr.2** Blokové znázornění realizace | ||
+ | |||
+ | Programování samotného procesoru probíhá v jazyce C (lze i C++) v prostředí SDK, není tak nutno zasahovat do popisu ve VHDL. V rámci VHDL je vytvořen pouze tzv, HDL wrapper -"black box", který má patřičné vstupy a výstupy. | ||
| | ||
==== Generovaná data pro paměť ==== | ==== Generovaná data pro paměť ==== | ||
- | Následujícím kódem je pomocí Matlabu vygenerováno celkem 200 000 vzorků data, data představují v tomto případě sinusovku (v absolutní hodnotě). Šířka dat je 32 bitů, což odpovídá nastavení šířce paměti BRAM (taktéž 32 bitů). Parametr radix na prvním řádku udává v jakém vyjádření data jsou, v tomto případě se jedná o desítkovou soustavu. Formát souboru je typu *.coe, jeho struktura je dána. Primárně slouží pro uložení koeficientů pro realizaci digitálních filtrů, nicméně ho lze použít i pro tyto účely. Takto vytvořený soubor lze již přímo použít v rámci vývojového prostředí Vivado. | + | Následujícím kódem je pomocí Matlabu vygenerováno celkem 200 000 vzorků data, data představují v tomto případě sinusovku (v absolutní hodnotě). Šířka dat je 32 bitů, což odpovídá nastavení šířce paměti BRAM (taktéž 32 bitů). Parametr radix na prvním řádku udává v jakém vyjádření data jsou, v tomto případě se jedná o desítkovou soustavu. Formát souboru je typu *.coe, jeho struktura je dána. Primárně slouží pro uložení koeficientů při realizaci digitálních filtrů, nicméně ho lze použít i pro tyto účely. Takto vytvořený soubor lze již přímo použít v rámci vývojového prostředí Vivado. |
<code> | <code> | ||
header1 = 'memory_initialization_radix=10;'; | header1 = 'memory_initialization_radix=10;'; | ||
Řádek 108: | Řádek 110: | ||
=== Inicializace rozhraní=== | === Inicializace rozhraní=== | ||
- | V rámci funkce **eth_init()** je vyčtena MAC adresa, nastaveny patřičné IP adresy (pokud se nevyužije DHCP), inicializován lwIP stack, přiřazení rozhraní pro obsluhu MAC jádrem (obstarává FPGA), zprovozněno patřičné rozhraní, a konečně, pokud je využit DHCP) získána IP adresa desky, maska a adresa síťového rozhraní (gateway). V případě DHCP se pokouší získat IP adresa, pokud je po vypršení času adresa nulová (=adresu se nepodařilo získat) je nastavena adresa defaultní. Funkce pro DHCP jsou překládány pouze v případě, že je DHCP využit (podmíněný překlad). | + | V rámci funkce **eth_init()** je vyčtena MAC adresa, nastaveny patřičné IP adresy (pokud se nevyužije DHCP), inicializován lwIP stack, přiřazení rozhraní pro obsluhu MAC jádrem (obstarává FPGA), zprovozněno patřičné rozhraní, a konečně, (pokud je využit DHCP) získána IP adresa desky, maska a adresa síťového rozhraní (gateway). V případě DHCP se pokouší získat IP adresa, pokud je po vypršení času adresa nulová (=adresu se nepodařilo získat) je nastavena adresa defaultní. Funkce pro DHCP jsou překládány pouze v případě, že je DHCP využit (podmíněný překlad). |
<code c> | <code c> | ||
Řádek 170: | Řádek 172: | ||
==== Čtení z paměti, plnění bufferu a odesílání dat ==== | ==== Čtení z paměti, plnění bufferu a odesílání dat ==== | ||
- | Pro odeslání dat je vytvořena funkce **udp_transfer()**, ta je aktuálně volána z hlavní smyčky ve funkci main po stisku tlačítka. Funkce je blokující, tzn. že se vrací zpět to funkce main po odeslání všech dat z paměti. V cílové aplikaci bude ve funkci main pouze pouze konfigurace A/D převodníky (bez jeho další obsluhy) a zasílání dat, v tomto případě není třeba vykonávat po dobu odesílání jiné instrukce. | + | Pro odeslání dat je vytvořena funkce **udp_transfer()**, ta je aktuálně volána z hlavní smyčky ve funkci main po stisku tlačítka. Funkce je blokující, tzn. že se vrací zpět to funkce main po odeslání všech dat z paměti. V cílové aplikaci bude ve funkci main pouze pouze konfigurace A/D převodníku (bez jeho další obsluhy) a zasílání dat, v tomto případě není třeba vykonávat po dobu odesílání jiné instrukce. Přístup do BRAM zajišťuje [[https://www.xilinx.com/products/intellectual-property/axi_bram_if_ctlr.html|AXI BRAM Controller]]. |
<code c> | <code c> | ||
Řádek 228: | Řádek 230: | ||
Data z BRAM jsou při odesílání postupně rozdělena do bloků velikosti 1024 Bytů, pokud má slovo v paměti šířku 32 bitů, je tedy naráz odesláno 256 vzorků. Teoreticky lze však může být blok velký až 65 507 Bytů nebo i větší při použití JumboFramu (lwIP jej podporuje). Jelikož je UDP nespolehlivý protokol, je třeba volit kompromis mezi přijatelnou ztrátou a zbytečnou režií při malém užitečném obsahu v rámci. | Data z BRAM jsou při odesílání postupně rozdělena do bloků velikosti 1024 Bytů, pokud má slovo v paměti šířku 32 bitů, je tedy naráz odesláno 256 vzorků. Teoreticky lze však může být blok velký až 65 507 Bytů nebo i větší při použití JumboFramu (lwIP jej podporuje). Jelikož je UDP nespolehlivý protokol, je třeba volit kompromis mezi přijatelnou ztrátou a zbytečnou režií při malém užitečném obsahu v rámci. | ||
- | Parametr XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR udává první dostupnou přístupnou adresu. Při ladění však bylo zjištěno, že první vzorek je až na druhé pozici v paměti, proto. Funkce postupně ukládá do bufferu buffer_send jednotlivé "vzorky" z paměti, v okamžiku dosažení nastavených počtu vzorků v jednom paketu (SAMPLES_IN_PACKET) je alokován bufferu lwipu stacku a data jsou předána k odeslání. V případě, že poslední paket není zcela naplněn, zůstávají na posledních pozicích předešlá data, to je řešeno už snadno v aplikaci na PC. | + | Parametr XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR udává první dostupnou přístupnou adresu. Při ladění však bylo zjištěno, že první vzorek je až na druhé pozici v paměti, proto za zaveden "offset". Funkce postupně ukládá do bufferu buffer_send jednotlivé "vzorky" z paměti, v okamžiku dosažení nastavených počtu vzorků v jednom paketu (SAMPLES_IN_PACKET) je alokován buffer lwipu stacku a data jsou předána k odeslání. V případě, že poslední paket není zcela naplněn, zůstávají na posledních pozicích předešlá data, to je řešeno už snadno v aplikaci na PC. |
Oproti klasické aplikaci, kde jsou data odesílána kontinuálně (například konvertor) nebo je známa pozice posledního uloženého vzorku ve formě ukazatele, je v této aplikaci odeslána pevný počet vzorků. V cílové aplikaci pak bude ukazatel na poslední uložený vzorek znám. | Oproti klasické aplikaci, kde jsou data odesílána kontinuálně (například konvertor) nebo je známa pozice posledního uloženého vzorku ve formě ukazatele, je v této aplikaci odeslána pevný počet vzorků. V cílové aplikaci pak bude ukazatel na poslední uložený vzorek znám. | ||
Řádek 242: | Řádek 244: | ||
Příklad možných nastavení v rámci BSP: | Příklad možných nastavení v rámci BSP: | ||
- | {{ :2017:lwip_stack:bsp_lwip.png?600 |}} | + | {{ :2017:lwip_stack:bsp_lwip.png?800 |}} |
- | **Obr. 3** Možná nastavená lwIP stacku v rámci BSP | + | **Obr. 3** Možná nastavení lwIP stacku v rámci BSP |
Řádek 251: | Řádek 253: | ||
Funkčnost zasílání dat přes protokol UDP byla ověřena jak aplikací Wireshark, tak i pomocí skriptu napsaném v jazyce Python. | Funkčnost zasílání dat přes protokol UDP byla ověřena jak aplikací Wireshark, tak i pomocí skriptu napsaném v jazyce Python. | ||
- | {{ :2017:lwip_stack:wireshark_output.png?1000 |}} | + | Na obrázku 4 jsou zobrazeny detailní informace o prvním přijatém paketu (v prvním paketu lze najít k porovnání očekávaná data). Konkrétně: |
- | **Obr. 4** Zachycené pakety programem Wireshark | + | |
- | + | ||
- | Na obrázku 3 jsou zobrazeny detailní informace o prvním přijatém paketu (v prvním paketu lze najít k porovnání očekávaná data). Konkrétně: | + | |
* Délka rámce 1066 B, z toho délka UDP datagramu 1032 B, a z toho užitečných dat 1024 B. Hodnota 1024 B správně představuje celkem 256 32bitových vzorků. | * Délka rámce 1066 B, z toho délka UDP datagramu 1032 B, a z toho užitečných dat 1024 B. Hodnota 1024 B správně představuje celkem 256 32bitových vzorků. | ||
* Adresa TCP portu 50000 | * Adresa TCP portu 50000 | ||
Řádek 261: | Řádek 260: | ||
* první data tvoří skutečně první vzorky uložené v paměti (134930, ...) - Little endian. | * první data tvoří skutečně první vzorky uložené v paměti (134930, ...) - Little endian. | ||
+ | {{ :2017:lwip_stack:wireshark_output.png?1000 |}} | ||
+ | **Obr. 4** Zachycené pakety programem Wireshark | ||
- | Pro Python skript je použito prostředí Spyder (dnes Anaconda). Jedná se o skriptovací jazyk, není zde standardní kompilace, tak, jak je tomu například u C++ nebo C#. K vykonávání programu je třeba tzv. interpret. Existuje i možnost vytvořit spustitelný soubor, stejně tak je možné vytvořit patřičné GUI. | ||
- | Využita je knihovna Socket (podporuje UDP i TCP). Skript čte ve smyčce přijímací buffer. Funkce socket vrací data jako tzv. bytearray, je tedy nutno vždy seskupit 4 B pro 32bitovou proměnou integer. Data jsou ukládána do CSV souboru společně s "pořadovým" číslem vzorků - lze nehradit například časem. | ||
+ | Pro Python skript je použito prostředí Spyder (dnes Anaconda). Jedná se o skriptovací jazyk, není zde standardní kompilace, tak, jak je tomu například u C++ nebo C#. K vykonávání programu je třeba tzv. interpret. Existuje i možnost jak vytvořit spustitelný soubor, stejně tak je možné vytvořit patřičné GUI. | ||
+ | Využita je knihovna Socket (podporuje UDP i TCP). Skript čte ve smyčce přijímací buffer. Funkce socket vrací data jako tzv. bytearray, je tedy nutno vždy seskupit 4 B pro 32bitovou proměnou integer. Data jsou ukládána do CSV souboru společně s "pořadovým" číslem vzorku - lze nahradit například časem. Skript končí po vypršení timeoutu, kdy již nejsou zasílána další data. | ||
+ | |||
+ | <code python> | ||
+ | import socket | ||
+ | import csv | ||
+ | import struct | ||
+ | |||
+ | |||
+ | host="0.0.0.0" | ||
+ | port = 50000 | ||
+ | samples = 200000 | ||
+ | |||
+ | csvf = 'test.csv' | ||
+ | |||
+ | s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) | ||
+ | s.bind((host,port)) | ||
+ | |||
+ | addr = (host,port) | ||
+ | buf=10000000 | ||
+ | array=[] | ||
+ | |||
+ | sample=1; | ||
+ | with open(csvf, 'w', newline='', encoding='ascii') as csv_handle: | ||
+ | csv_writer = csv.writer(csv_handle, delimiter=',') | ||
+ | data,addr = s.recvfrom(buf) | ||
+ | try: | ||
+ | while(data): | ||
+ | length=(len(data)) | ||
+ | x=0 | ||
+ | for i in range(0,length//4): | ||
+ | value=struct.unpack("I", bytearray(data[x:x+4])) | ||
+ | array.append(value) | ||
+ | data_list=[x for xs in array for x in xs] | ||
+ | data_list.append(sample) | ||
+ | sample=sample+1 | ||
+ | array=[] | ||
+ | csv_writer.writerow(data_list) | ||
+ | x=x+4 | ||
+ | s.settimeout(5) | ||
+ | if (sample==samples+1): | ||
+ | break; | ||
+ | data,addr = s.recvfrom(buf) | ||
+ | except (socket.timeout,KeyboardInterrupt, SystemExit): | ||
+ | print ("Operation completed") | ||
+ | raise | ||
+ | </code> | ||
------- | ------- | ||
Řádek 279: | Řádek 325: | ||
Byť se v rámci internetu nepodařilo nalézt vhodné inspirativní kódy s využitím TCP nebo UDP, ukázalo se použití lwIP stacku, po seznámení s jeho knihovnami, jako poměrně jednoduché. Po stránce hardwarové nakonec nebylo zapotřebí řešit speciální konfiguraci obvodu fyzické vrstvy, pokud se spokojíme s rychlostí 100Mbit (nutno nastavit v rámci konfigurace BSP). | Byť se v rámci internetu nepodařilo nalézt vhodné inspirativní kódy s využitím TCP nebo UDP, ukázalo se použití lwIP stacku, po seznámení s jeho knihovnami, jako poměrně jednoduché. Po stránce hardwarové nakonec nebylo zapotřebí řešit speciální konfiguraci obvodu fyzické vrstvy, pokud se spokojíme s rychlostí 100Mbit (nutno nastavit v rámci konfigurace BSP). | ||
- | V rámci další práce se počítá s využitím RTOS (Free RTOS) a implementací protokolu TCP namísto protokolu UDP, případně zvýšení rychlsoti na 1Gbit, bude-li to třeba. | + | V rámci další práce se počítá s využitím RTOS (Free RTOS) a implementací protokolu TCP namísto protokolu UDP, případně zvýšení rychlosti na 1Gbit, bude-li to třeba. Počítáno je rovněž s rozšířením aplikace v Pythonu - větší možnosti konfigurace a GUI. |
Použitá vývojová prostředí: Spyder (Python), Vivado, SDK Eclipse (C), Matlab | Použitá vývojová prostředí: Spyder (Python), Vivado, SDK Eclipse (C), Matlab | ||
Řádek 292: | Řádek 338: | ||
====== Reference ====== | ====== Reference ====== | ||
[1] lwIP Stack dokumentace: http://www.nongnu.org/lwip/2_0_x/group__udp__raw.html | [1] lwIP Stack dokumentace: http://www.nongnu.org/lwip/2_0_x/group__udp__raw.html | ||
- | |||
[2] UDP komunikace v Pythonu: https://wiki.python.org/moin/UdpCommunication | [2] UDP komunikace v Pythonu: https://wiki.python.org/moin/UdpCommunication |