- microcontroller पर TCP/IP stack को खुद से बनाने की पहली कड़ी के रूप में, STM32F401 Nucleo और Wiznet W5100 shield को जोड़कर Ethernet frame भेजने की कोशिश की गई
- W5100 की hardware TCP/IP क्षमता का उपयोग नहीं किया गया; केवल MAC Raw mode का उपयोग करके chip को frame भेजने के लिए जरूरी low-level processing ही सौंपी गई
- पहली बाधा Arduino Ethernet shield की SPI wiring का Nucleo से मेल न खाना था; ICSP header routing की जांच के बाद board पर modified wiring करके इसे हल किया गया
- इसके बाद chip select timing से जुड़ी लगने वाली MISO की अजीब responses और Wireshark में garbage packets दिखने के दौरान, logic analyser और reference implementation से तुलना मुख्य debugging साधन बने
- अंतिम कारण
w5100_write16()में bug था, जो दूसरे byte को अगले address पर लिखने के बजाय उसी address पर फिर से लिख रहा था; SPI capture CSV analysis tool बनाने की वजह से अंततः सही packet transmission तक पहुंचा जा सका
अपना TCP/IP stack बनाने की शुरुआत
- लक्ष्य microcontroller पर TCP/IP stack को बिल्कुल नीचे से implement करने वाली “Networking from scratch” series शुरू करना है
- इस चरण का ऊपरी परिणाम पहला Ethernet packet भेजना है, लेकिन असली focus hardware और driver के बीच पैदा हुए bug को trace करने की प्रक्रिया पर है
- इस्तेमाल किया गया board STM32F401 आधारित Nucleo development board है
- ARM Cortex-M4
- अधिकतम 84MHz operation
- 96KiB RAM
- कई packets रखने के लिए memory पर्याप्त मानी गई
Ethernet और W5100 की भूमिका
- Ethernet केवल एक port या frame format नहीं है, बल्कि physical layer hardware, signalling method, bus collision handling और frame layout तक को शामिल करने वाली technology और standards की family है
- Ethernet signal processing जटिल होती है, इसलिए आमतौर पर dedicated ASIC frame-level data लेकर cable के electrical signal processing को संभालता है
- project में Wiznet W5100 chip वाला Arduino Ethernet shield इस्तेमाल किया गया
- इस्तेमाल किया गया board कम कीमत वाला clone था, इसलिए सही operation के लिए modifications की जरूरत पड़ी
- W5100 एक ऐसा chip है जिसमें Ethernet ASIC के साथ hardware TCP/IP stack built-in है
- यह 4 “socket” देता है
- इसे TCP, UDP, IP और “MAC Raw” levels पर configure किया जा सकता है
- अपना TCP/IP stack implement करने के उद्देश्य से W5100 की TCP/IP सुविधाओं का उपयोग नहीं किया गया, और एक socket को केवल MAC Raw mode में इस्तेमाल किया गया
- user Ethernet frame देता है
- W5100 वास्तविक transmission करता है
- preamble और start-of-frame marker electrical-level elements हैं, इसलिए chip उन्हें handle करता है
- 32-bit CRC भी W5100 calculate करता है
समस्या 1: SPI signal W5100 तक नहीं पहुंच रहा
- W5100 के साथ data exchange SPI के जरिए होता है
- MOSI: main chip यानी microcontroller का output
- MISO: W5100 का output
- clock: data reference clock
- chip select: यह बताने वाला signal कि subordinate chip से communication चल रहा है
- W5100 datasheet SPI के ऊपर 4-byte command protocol define करती है
- 1-byte operation
- 2-byte 16-bit big-endian address
- 1-byte value
- operation लिखने के लिए
0xf0या पढ़ने के लिए0x0fहै- अन्य values valid नहीं हैं और उन्हें ignore किया जाना चाहिए
- W5100 हर byte को clock out करते समय MISO पर known value लौटाता है, इसलिए communication issue पहचानना आसान होता है
- read command में चौथा byte दिए गए address से पढ़ी गई value होता है
- शुरुआत में MOSI पर command भेजने पर भी MISO पर garbage values दिख रही थीं
- कारण यह था कि Arduino Ethernet shield ने SPI signals को Arduino standard header की जगह ICSP 6-pin header पर route किया था
- official Arduino boards में अंदर ही ICSP और standard header के SPI signals जुड़े होते हैं, इसलिए समस्या नहीं होती
- Nucleo board पर ICSP header नहीं था, इसलिए भेजे गए SPI signals W5100 तक नहीं पहुंचे
- multimeter से power rails और SPI signals के बीच resistance मापकर connection issue की पुष्टि की गई
- infinite resistance आने पर पता चलता है कि wiring कटी हुई है
- soldering और enamelled copper wire से Nucleo और W5100 के बीच signals को सीधे जोड़ा गया
समस्या 2: chip select timing और MISO की अजीब response
- SPI signals सच में connect हो जाने के बाद W5100 से communication संभव हो गया, और अगले चरण तक implementation किया गया
- W5100 raw Ethernet transmission setup
- MAC address setup
- TX/RX memory segment setup
- test Ethernet frame को TX memory में लिखना और transmission trigger करना
- Nucleo और shield को CAT5 cable से laptop से जोड़ा गया और Wireshark चलाया गया, लेकिन packet दिखाई नहीं दिया
- low-level problems में error message या stack trace नहीं होती; वे “electrons को हिलाया, पर अपेक्षित चीज नहीं हुई” जैसी होती हैं, इसलिए trace करना मुश्किल होता है
- debugging के लिए logic analyser इस्तेमाल किया गया
- यह कई channels पर digital signals के high/low transitions sample करता है
- software SPI bus जैसे signal groups को interpret करके transaction bytes दिखाता है
- इसे CSV जैसे structured formats में export किया जा सकता है
- इस्तेमाल किया गया उपकरण Saleae Logic 8 था
- करीब €10 वाले low-cost analyser भी Saleae Logic2 के साथ काम कर सकते हैं, लेकिन सही और consistent operation में tradeoff होता है
- पहला SPI command सही दिख रहा था
0xf0 0x00 0x00 0x80- address
0x0000के Mode Register में top bit set करके software reset trigger करना - MISO ने
0x00से0x03तक normal response दिया
- अगले read command में अजीब response दिखी
- MOSI:
0x0f 0x00 0x00 0x00 - MISO:
0x03 0xff 0xff 0xff
- MOSI:
- चूंकि
0x03पिछली transaction की आखिरी value थी, इसलिए W5100 की internal state या timing issue पर शक हुआ - SPI clock datasheet में दिए गए लगभग 14MHz maximum से काफी कम इस्तेमाल हो रही थी, इसलिए इसे कारण नहीं माना गया
- इसके बजाय माना गया कि chip select बहुत जल्दी high हो जा रहा है और chip खराब state में चला जाता है, इसलिए state change से पहले कुछ microseconds की delay डाली गई
- इसके बाद MISO response normal हो गई
- configuration values दोबारा पढ़ने पर भी reasonable values मिलीं
- इस समस्या का exact cause अभी पूरी तरह समझ में नहीं आया
- datasheet के page 66 पर SPI timing diagram में दिए गए chip select constraints पूरे हो रहे थे
- exact boundary conditions को फिर से investigate किया जा सकता है, लेकिन अभी project progress को प्राथमिकता दी गई
समस्या 3: Wireshark में दिखे garbage packets
- Wireshark फिर से चलाने पर packets दिखे, लेकिन वे intended packets नहीं थे
- microcontroller द्वारा transmit command भेजते ही, डाले गए packet से कहीं बड़ा raw Ethernet packet garbage data से भरा हुआ दिखाई दिया
- केवल datasheet और specs देखकर implement करने की योजना छोड़कर, इस चरण में working implementation से तुलना करने का फैसला किया गया
- Arduino reference implementation जल्दी बनाने के लिए उपयोगी है
- Arduino, libraries और लगभग 5 lines of code से complex behavior verify किया जा सकता है
- raw Ethernet packet transmission library खोजने में अधिक समय लगा
- अधिकतर Arduino users networking functionality को scratch से implement करने की कोशिश नहीं करते
- official library ने public API से raw packet transmission support हटा दिया था
- packet send/receive को minimum level पर करने वाला GitHub project W5100MacRaw मिला
- उस source और अपनी implementation की तुलना की गई, लेकिन तुरंत दिखने वाले differences निर्णायक नहीं थे
- register read/write order अलग था
- कुछ read/write केवल एक तरफ थे
- आमतौर पर अगर order dependency होती है तो datasheet में लिखा होता है
- read/write order को reference implementation जैसा करने के बाद भी Wireshark में garbage packets ही दिखते रहे
एक छोटे tool से सामने आया असली bug
- अगली strategy Saleae Logic2 के SPI capture CSV को parse करके register read/write list के रूप में फिर से दिखाने वाला tool लिखना था
- tool Python में बनाया गया, और कुल length 200 lines से थोड़ी ज्यादा थी
- ज्यादातर हिस्सा datasheet से copy किए गए register names और addresses थे
- इसे लिखने में करीब 1 घंटा लगा
- input और output को स्पष्ट करने के लिए argument parsing भी जोड़ी गई
- Arduino reference implementation और अपनी implementation, दोनों से SPI capture लेकर CSV में export किया गया, फिर tool से process करके diff किया गया
- समस्या
w5100_write16(u16 address, u16 value)convenience function में थी- W5100 के कई registers 16-bit values हैं
- command format एक बार में केवल 8-bit लिख सकता है, इसलिए high/low byte में split करके लिखना पड़ता है
- यह function दूसरे byte को
address + 1पर लिखने के बजाय उसी address पर फिर से लिख रहा था
- Arduino reference log ने
S0_TX_WR0औरS0_TX_WR1में अलग-अलग लिखा थाS0_TX_WR0 [0x0424] 0x00S0_TX_WR1 [0x0425] 0x3c
- अपने log में दोनों writes
S0_TX_WR0 [0x0424]पर ही जा रहे थेS0_TX_WR0 [0x0424] 0x00S0_TX_WR0 [0x0424] 0x3c
- यह register Socket 0 transmit write pointer था
- W5100 इस 16-bit register को क्रम से पढ़ने
- TX memory में bytes लिखने
- नया write pointer फिर से लिखने
- और socket send command भेजने वाला flow expect करता है
- दूसरे byte को न लिखे हुए send चलाने पर chip undefined abnormal state में चला गया, और Wireshark में random result जैसे packets दिखाई दिए
- function fix करने पर test packet Wireshark में सही दिखाई देने लगा
debugging tools बनाने में समय लगाने की value
- पहला Ethernet packet भेजना अपने आप में बहुत बड़ा achievement नहीं है, लेकिन project में यह एक स्पष्ट success moment माना जा सकता है
- bug को पीछे जाकर समझना personal project में मजेदार होता है, और tooling बनाना व debugging space explore करना लगभग हमेशा valuable होता है
- कुछ professional environments में direct deliverable न बनाने वाले काम को waste मानने की प्रवृत्ति होती है
- development process के JIRA-style छोटे हिस्सों में बंटने से, checkbox सीधे न भरने वाला काम कम value का माना जाता है
- system को अभी पर्याप्त समझे बिना tool बनाना test लिखने जितना, और कभी-कभी उससे भी ज्यादा important हो सकता है
- debugging scientific method के implementation जैसी है
- data इकट्ठा करना
- prediction बनाना
- experiment से prediction validate करना
- नए data से prediction update करना
- यह project आगे और ऊंचे abstraction level की समस्याओं की ओर बढ़ता है
- RFC को गलत समझना
- multitasking code लिखना
- नए bugs के साथ आगे बढ़ना
1 टिप्पणियां
Hacker News की रायें
सीधे छोटे टूल बना सकने की क्षमता एक सुपरपावर है, और अक्सर जिसे 10x प्रोग्रामर कहा जाता है, उसके मूल में यही होती है
अफसोस कि ऐसी स्किल आम तौर पर नज़र से दूर, चुपचाप इस्तेमाल होती है
लेकिन जिस व्यक्ति में जिज्ञासा हो और जो second-order effects या जल्द जरूरत पड़ने वाले features के बारे में सोचता हो, उसके लिए ऐसा न करने वाले developer के साथ काम करना काफी तकलीफदेह होता है
हाल में जिसके साथ काम किया, वह बुरा इंसान नहीं था, लेकिन मैं एक ऐसा project साफ कर रहा हूँ जिसमें ticket ने जो कहा था, सिर्फ वही 100% implement किया गया। नया project पुराने software की समस्या को replace करने वाला था, लेकिन उन्हीं कारणों से पुरानी सारी समस्याएँ जस की तस बची रहीं
हालांकि 10x developer वाला शब्द थोड़ा buzzword जैसा लगता है। इससे ऐसे लापरवाह developer याद आते हैं जो बिना supervision के “काम पूरा” तो कर देते हैं, लेकिन भारी लागत छोड़ जाते हैं; सब कुछ silo में बंद हो जाता है और बाद में कोई और उसे छुए या वह व्यक्ति चला जाए तो वह ताश के पत्तों के घर की तरह ढह जाता है
आदर्श रूप से सभी को exploration time मिलना चाहिए, लेकिन जब senior manager कहे कि “इसे और जल्दी खत्म करना है”, तब भी इसे सख्ती से बचाकर रखने वाला manager चाहिए। अगर दूसरी team का manager कहे, “क्या उस team को खोया हुआ समय compensate करने के लिए और लोग hire करने दिए जाएँगे? हमें भी वही manpower चाहिए,” तो बात और मुश्किल हो जाती है
आखिरकार संगठन-स्तर की व्यवस्था चाहिए, और Google तक ने 20% time छोड़ दिया
“अनैतिक” समाधान यह है कि development estimates में ऐसा समय थोड़ा शामिल करके रखा जाए
जहाँ लागू हो, अपने टूल बनाना उपयोगी है, लेकिन productive developer वह भी माना जाता है जो सबसे कम code लिखता है और जो पहले से मौजूद है उसका इस्तेमाल करता है। इस लेख के उदाहरण से कहें तो, किसी को खुद से TCP stack दोबारा बनाने की जरूरत नहीं है; पहले से शानदार implementations मौजूद हैं
फिर भी, खुद बनाना गहरी समझ पाने का सबसे अच्छा तरीका हो सकता है, और गहरी समझ उस मिथकीय 10x developer का एक घटक है। बस यह उम्मीद नहीं करूँगा कि employer उस प्रक्रिया के लिए पैसे देगा
आम तौर पर अगर काम एक दिन के भीतर खत्म होने वाला हो तो मैं बस कर देता हूँ। यह कभी गलत साबित नहीं हुआ और न ही कभी waste हुआ। सबसे खराब स्थिति में बाद में उस code को कहीं और copy-paste कर देता हूँ
फिर वे कहने लगे कि इसे non-programmers के इस्तेमाल लायक बना दो, और अचानक वे tools अनुमान से कहीं ज्यादा बड़ा काम बन गए
title काफी अस्पष्ट है; यह लेख microcontroller के लिए TCP/IP और Ethernet framing stack को scratch से बनाने वाली series की शुरुआत है
लेखक W5100 chip इस्तेमाल करता है, जो TCP/IP खुद handle कर सकती है, लेकिन पहले से बने Ethernet frames पास करने का तरीका भी support करती है। हालांकि preamble और CRC calculation chip ही करती है
लेख का ज्यादातर हिस्सा chip से खुद communicate करने और test packet भेजने की प्रक्रिया के बारे में है। यह hardcoded packet लगता है, लेकिन लेख में साफ नहीं कहा गया
निजी तौर पर मैं उम्मीद कर रहा था कि यह किसी बेहूदा hardware पर Ethernet को bit bang करने के बारे में होगा
एक तरकीब यह है कि आम PHY + MagJack board को modify करके RP2040 से clock signal बनवाया जाए। इससे signal synchronized रहता है और RMII को oversample करने की जरूरत नहीं पड़ती
अगर सिर्फ 10Mb/s चाहिए तो और भी गंदे तरीके से हो सकता है। मैं अब भी RP2040 में DVI/HDMI video output और Ethernet को जोड़ने वाले “modern” glass terminal का इंतजार कर रहा हूँ। सिर्फ telnet से भी काम चल जाएगा, SSH शायद मुश्किल होगा
हाल ही में मेरा career कुछ असामान्य तरीके से Ethernet-केंद्रित FPGA engineering की ओर मुड़ा
यह मजेदार सफर रहा, और आखिरकार मैं खुद Hard MAC IP design करने और custom PHY IP के जरिए packets भेजने तक पहुँचा
जो लोग इस challenge को “hard mode” में करना चाहते हैं, उन्हें मैं इसकी जोरदार सिफारिश करता हूँ। Networking users से इतनी abstract कर दी गई है कि Ethernet card, modem और switch packet के हर हिस्से को कैसे assemble और disassemble करते हैं, और PHY/PCS link पर signal कैसे recover करता है, यह समझना सचमुच बहुत मूल्यवान था
debugging और verification के लिए Verilator से बने simulation version को Linux TUN/TAP device से connect करने लायक बनाया था, और physical hardware occupy किए बिना developer machine से सीधे connect किया जा सकता था। Hardware सिर्फ एक था, इसलिए यह खास तौर पर उपयोगी था
मेरी networking knowledge OSI model की बेहद basic understanding तक ही है, लेकिन networking की दुनिया में गहराई से उतरना चाहता हूँ
MOSI/MISO की reinterpretation पहली बार देखी। master out/slave in की जगह main out/subordinate in कहना
इससे pin names MOSI/MISO ही कहे जा सकते हैं, इसलिए शायद मैं भी यही इस्तेमाल करूँगा। COPI/CIPO(controller out/peripheral in) विकल्प दिमाग में टिकता नहीं था
लेखक W5100 Ethernet shield और STM32F401 क्यों इस्तेमाल कर रहे हैं, यह थोड़ा अजीब लगा। इसी तरह आसानी से STM32F407 board इस्तेमाल किया जाए तो उसमें built-in Ethernet MAC होता है, और उसे सस्ते Ethernet PHY board के साथ जोड़कर develop किया जा सकता है
Ethernet example projects भी बहुत हैं और यह STM32F401 जितना ही develop करने में आसान है
साथ ही “Ethernet signal processing की जटिलता के कारण आम तौर पर dedicated ASIC इस्तेमाल किया जाता है” वाली व्याख्या microcontroller संदर्भ में कुल मिलाकर सही नहीं लगती। कई मामलों में Ethernet capability STM32F407 या ESP32 की तरह microcontroller के अंदर built-in peripheral के रूप में होती है
built-in Ethernet peripheral वाला chip इस्तेमाल करना निश्चित रूप से ज़्यादा समझदारी है। हालांकि यह W5100 configuration की जटिलता को ST peripheral configuration की जटिलता से बदलने जैसा भी है
Networking code पहले से ही actual chip को driver interface (read/write/ioctl जैसी form) के पीछे abstract कर चुका है, इसलिए porting काफ़ी सरल होनी चाहिए
इस series में STM32F407 पर विचार करूंगा
Ethernet packets नहीं, frames से deal करता है
Packet एक IP concept है
हालांकि RFC 791 उस दौर का document है जब नीचे का L2 network 128-byte packets इस्तेमाल करने वाला ARPAnet होने की काफ़ी संभावना थी
आज यह distinction असल में लगभग निरर्थक होना चाहिए। अगर आप बड़े IP datagrams को कई L2 packets में भेजने के लिए IP fragmentation पर निर्भर हैं, तो कुछ गड़बड़ है। IPv6 तो network के अंदर fragmentation भी support नहीं करता
Practical काम में समय के साथ यह फर्क लगभग गायब हो गया है। आम तौर पर एक Ethernet frame में एक IP datagram होता है, और सभी उसे बस packet कह देते हैं
https://en.wikipedia.org/wiki/Ethernet_frame
अगर आप microcontroller पर wired Ethernet आज़माना चाहते हैं, तो कुछ बड़े STM32 Nucleo boards में 100Mbps Ethernet built-in है और करीब 25 dollars में काफ़ी सस्ते हैं
STM32Cube software पर राय mixed है, लेकिन यह काम करने वाला Ethernet communication example बना देता है
https://www.st.com/en/evaluation-tools/nucleo-f439zi.html
MQTT, HTTP client और server वगैरह साथ आते हैं
पिछली बार जब Cube-MX इस्तेमाल किया था, तो overall अनुभव बहुत खराब था। अगर फिर STM32 इस्तेमाल करूं, तो शायद stm32-hal या libopencm3 पसंद करूंगा। tool खुद और generated code में तरह-तरह के खराब bugs और edge cases थे, जिनकी debugging में कई दिन लग गए। शायद अब बेहतर हो गया हो
https://github.com/egnor/wt32-eth01
https://liliputing.com/waveshare-esp32-p4-nano-is-a-tiny-ris...
अभी देखा कि और भी सस्ते modules हैं। WT32-ETH01 जैसी चीज़ें performance में कम हो सकती हैं
अगर Linux पर सीधे network stack लिखकर देखना चाहते हैं, तो
socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))का उपयोग करके इस article से बहुत अलग नहीं abstraction level पर काम कर सकते हैंSOCK_RAWpackets packet data को बदले बिना device driver से भेजे/लिए जाते हैं। receive करते समय address parse होकर standardsockaddr_lladdress structure के रूप में पास होता हैsend करते समय user-provided buffer में physical layer header होना चाहिए, और वह packet destination address द्वारा बताए गए interface की network driver queue में बिना बदलाव चला जाता है
https://man7.org/linux/man-pages/man7/packet.7.html
यह VPN interface की तरह network stack में virtual interface जोड़ता है। वहीं packet socket उल्टा, आपको actual interface से सीधे communicate करने देता है
सही-सही कहें तो इसे Ethernet frame कहना चाहिए
अच्छा बनाया है। मैंने अभी पिछले 16 घंटे के काम के समय उल्टा काम करने में लगाए: Ethernet II (VLAN सहित), IPv4+6, UDP को automotive IP protocol तक parse करने वाला implementation
इसका उपयोग proprietary bus capture stream को समझने के लिए है, जिसमें Ethernet frames के अंदर nested Ethernet frames वगैरह आते हैं, और मैं उन्हें raw socket से receive करता हूं
ऐसे कामों में Wireshark और ChatGPT सच में बेहद कीमती हैं