कैसे एक माइक्रोकंट्रोलर बूट और स्टार्टअप, कदम से कदम?


19

जब सी कोड एक माइक्रोकंट्रोलर को लिखा, संकलित और अपलोड किया जाता है, तो माइक्रोकंट्रोलर चलना शुरू हो जाता है। लेकिन अगर हम इस अपलोडिंग और स्टार्टअप प्रक्रिया को धीमी गति से आगे बढ़ाते हैं, तो मुझे इस बारे में कुछ भ्रम है कि वास्तव में MCU (मेमोरी, सीपीयू, बूटलोडर) के अंदर क्या हो रहा है। यहाँ (शायद सबसे गलत है) अगर कोई मुझसे पूछे तो मैं क्या जवाब दूंगा:

  1. संकलित बाइनरी कोड USB के माध्यम से ROM (या EEPROM) को फ्लैश करने के लिए लिखा गया है
  2. बूटलोडर इस कोड के कुछ हिस्से को रैम में कॉपी करता है। अगर सही है, तो बूट-लोडर को कैसे पता चलता है कि कॉपी करना है (रैम में कॉपी करने के लिए रॉम का कौन सा हिस्सा है)?
  3. सीपीयू रोम और रैम से कोड के निर्देश और डेटा प्राप्त करना शुरू करता है

क्या यह गलत है?

क्या इस चरण के बारे में कुछ जानकारी के साथ बूटिंग और स्टार्टअप की इस प्रक्रिया को संक्षेप में प्रस्तुत करना संभव है, इस चरण में मेमोरी, बूटलोडर और सीपीयू कैसे बातचीत करते हैं?

मुझे कई बुनियादी स्पष्टीकरण मिल गए हैं कि कैसे BIOS के माध्यम से एक पीसी बूट करता है। लेकिन मैं माइक्रोकंट्रोलर स्टार्टअप प्रक्रिया के साथ फंस गया हूं।

जवाबों:


32

1) संकलित बाइनरी को प्रॉम / फ्लैश हां के लिए लिखा गया है। USB, सीरियल, i2c, jtag, आदि डिवाइस पर निर्भर करता है, जो कि डिवाइस द्वारा समर्थित है, बूट प्रक्रिया को समझने के लिए irrelevent।

2) यह आमतौर पर एक माइक्रोकंट्रोलर के लिए सच नहीं है, प्राथमिक उपयोग के मामले में रोम / फ्लैश और रैम में डेटा के निर्देश हैं। चाहे आर्किटेक्चर कोई भी हो। एक गैर-माइक्रोकंट्रोलर के लिए, आपका पीसी, आपका लैपटॉप, आपका सर्वर, प्रोग्राम को गैर-वाष्पशील (डिस्क) से कॉपी किया जाता है, फिर राम वहां से चला जाता है। कुछ माइक्रोकंट्रोलर आपको राम के साथ-साथ, यहां तक ​​कि कटाई का भी दावा करते हैं, भले ही यह परिभाषा का उल्लंघन करता दिखाई दे। हार्वर्ड के बारे में कुछ भी नहीं है जो आपको निर्देश के पक्ष में राम की मैपिंग करने से रोकता है, आपको बस शक्ति होने के बाद निर्देश प्राप्त करने के लिए एक तंत्र की आवश्यकता होती है (जो परिभाषा का उल्लंघन करता है, लेकिन हार्वर्ड सिस्टम को ऐसा करना होगा जो अन्य उपयोगी हो माइक्रोकंट्रोलर की तुलना में)।

3) की तरह।

प्रत्येक सीपीयू एक नियतात्मक में "बूट" करता है, जैसा कि डिजाइन किया गया है। सबसे आम तरीका एक वेक्टर टेबल है जहां पॉवर अप करने के बाद चलने वाले पहले निर्देशों के लिए पता रीसेट वेक्टर में हैं, एक पता जिसे हार्डवेयर पढ़ता है फिर उस पते का उपयोग चालू करना शुरू करता है। अन्य सामान्य तरीका यह है कि प्रोसेसर को बिना किसी जाने-पहचाने पते पर वेक्टर तालिका के बिना निष्पादित करना शुरू करना है। कभी-कभी चिप में "पट्टियाँ" होंगी, कुछ पिन जिन्हें आप रीसेट जारी करने से पहले उच्च या निम्न टाई कर सकते हैं, जो तर्क कई तरीकों से बूट करने के लिए उपयोग करता है। आपको सिस्टम के बाकी हिस्सों से खुद को सीपीयू, प्रोसेसर कोर को अलग करना होगा। यह समझें कि सीपीयू कैसे संचालित होता है, और फिर समझें कि चिप / सिस्टम डिजाइनरों के पास सीपीयू के बाहर के आसपास सेटअप एड्रेस डिकोडर है ताकि cpus एड्रेस स्पेस का कुछ हिस्सा एक फ्लैश के साथ संचार करता है। और कुछ राम के साथ और कुछ बाह्य उपकरणों (uart, i2c, spi, gpio, आदि) के साथ। आप चाहें तो वही सीपीयू कोर ले सकते हैं, और इसे अलग तरह से लपेट सकते हैं। यह वह चीज है जो आपको तब मिलती है जब आप कुछ हथियार खरीदते हैं या आधारित होते हैं। हाथ और कूल्हे सीपीयू कोर बनाते हैं, जो लोग चिप खरीदते हैं और अपने सामान को इधर-उधर लपेटते हैं, विभिन्न कारणों से वे उस सामान को ब्रांड से ब्रांड तक नहीं बनाते हैं। यही कारण है कि जब यह कोर के बाहर कुछ भी आता है तो शायद ही कभी एक सामान्य बांह का सवाल पूछ सकता है।

एक माइक्रोकंट्रोलर एक चिप पर एक सिस्टम बनने का प्रयास करता है, इसलिए इसकी गैर-वाष्पशील मेमोरी (फ्लैश / रोम), वाष्पशील (श्रम), और सीपीयू सभी एक ही चिप पर परिधीयों के मिश्रण के साथ होते हैं। लेकिन चिप को आंतरिक रूप से इस तरह से डिज़ाइन किया गया है कि फ्लैश को उस सीपीयू के एड्रेस स्पेस में मैप किया जाता है जो उस सीपीयू की बूट विशेषताओं से मेल खाता है। यदि उदाहरण के लिए cpu में 0xFFFC पते पर एक रीसेट वेक्टर है, तो उस पते पर प्रतिक्रिया करने के लिए फ़्लैश / रोम होना चाहिए जो कि हम 1 के माध्यम से प्रोग्राम कर सकते हैं), उपयोगी कार्यक्रमों के लिए पता स्थान में पर्याप्त फ़्लैश / रोम के साथ। एक चिप डिजाइनर उन आवश्यकताओं को पूरा करने के लिए 0xF000 पर शुरू होने वाले 0x1000 बाइट्स का फ्लैश चुन सकता है। और शायद उन्होंने राम की कुछ मात्रा को कम पते पर या शायद 0x0000, और परिधीयों को कहीं बीच में रख दिया।

सीपीयू की एक और वास्तुकला पता शून्य पर निष्पादित करना शुरू कर सकती है, इसलिए उन्हें विपरीत करने की आवश्यकता होगी, फ्लैश को जगह दें ताकि यह शून्य के आसपास एक पता सीमा का जवाब दे। उदाहरण के लिए 0x0000 से 0x0FFF कहें। और फिर कहीं और राम रख दिया।

चिप डिजाइनरों को पता है कि कैसे सीपीयू बूट करते हैं और उन्होंने वहां गैर-वाष्पशील भंडारण रखा है (फ्लैश / रोम)। इसके बाद सॉफ्टवेयर लोगों पर निर्भर है कि वह उस सीपीयू के जाने-माने व्यवहार से मेल खाने के लिए बूट कोड लिखें। आपको रीसेट वेक्टर में रीसेट वेक्टर पता और आपके बूट कोड को रीसेट वेक्टर में निर्धारित पते पर रखना होगा। टूलचिन यहां आपकी बहुत मदद कर सकता है। कभी-कभी, बिंदु के साथ जासूसी और आईडी या अन्य सैंडबॉक्स के साथ वे आपके लिए सबसे अधिक काम कर सकते हैं जो आप करते हैं वह उच्च स्तरीय भाषा (सी) में कॉलिस है।

लेकिन, हालांकि यह किया जाता है कि प्रोग्राम को फ्लैश / रोम में लोड किया गया है और इसे सीपीयू के हार्डवेयर्ड बूट व्यवहार से मेल खाना है। आपके कार्यक्रम के मुख्य भाग से पहले सी () और यदि आप अपने प्रवेश बिंदु के रूप में मुख्य का उपयोग करते हैं, तो कुछ चीजें करनी होंगी। एसी प्रोग्रामर मानता है कि जब एक प्रारंभिक मूल्य के साथ एक चर घोषित करते हैं, तो वे उम्मीद करते हैं कि वास्तव में काम करना है। खैर, चर, कांस्टेबल के अलावा, राम में हैं, लेकिन अगर आपके पास एक प्रारंभिक मूल्य है जो प्रारंभिक मूल्य गैर-वाष्पशील राम में होना है। तो यह .data सेगमेंट है और C बूटस्ट्रैप को .data सामान को फ़्लैश से राम तक कॉपी करने की आवश्यकता है (जहां आमतौर पर टूलकिन द्वारा आपके लिए निर्धारित किया जाता है)। आपके प्रारंभिक कार्यक्रम के बिना घोषित किए जाने वाले वैश्विक चरों को आपके कार्यक्रम के शुरू होने से पहले शून्य माना जाता है, हालांकि आपको वास्तव में यह नहीं मानना ​​चाहिए और शुक्र है कि कुछ संकलक अनधिकृत चर के बारे में चेतावनी देने लगे हैं। यह .bs सेगमेंट है, और C बूटस्ट्रैप शून्य, जो राम में है, सामग्री, शून्य, को गैर-वाष्पशील मेमोरी में संग्रहीत नहीं किया जाना है, लेकिन शुरुआती पता और कितना करता है। फिर से टूलचिन आपको यहां बहुत मदद करता है। और अंत में नंगे न्यूनतम आपको एक स्टैक पॉइंटर को सेटअप करने की आवश्यकता है क्योंकि सी प्रोग्राम स्थानीय चर और अन्य कार्यों को कॉल करने में सक्षम होने की उम्मीद करते हैं। फिर शायद कुछ अन्य चिप विशिष्ट सामान किया जाता है, या हम बाकी चिप विशिष्ट सामान को सी में करते हैं। गैर-वाष्पशील मेमोरी में संग्रहीत नहीं किया जाना चाहिए, लेकिन शुरुआती पता और कितना करता है। फिर से टूलचिन आपको यहां बहुत मदद करता है। और अंत में नंगे न्यूनतम आपको एक स्टैक पॉइंटर को सेटअप करने की आवश्यकता है क्योंकि सी प्रोग्राम स्थानीय चर और अन्य कार्यों को कॉल करने में सक्षम होने की उम्मीद करते हैं। फिर शायद कुछ अन्य चिप विशिष्ट सामान किया जाता है, या हम बाकी चिप विशिष्ट सामान को सी में करते हैं। गैर-वाष्पशील मेमोरी में संग्रहीत नहीं किया जाना चाहिए, लेकिन शुरुआती पता और कितना करता है। फिर से टूलचिन आपको यहां बहुत मदद करता है। और अंत में नंगे न्यूनतम आपको एक स्टैक पॉइंटर को सेटअप करने की आवश्यकता है क्योंकि सी प्रोग्राम स्थानीय चर और अन्य कार्यों को कॉल करने में सक्षम होने की उम्मीद करते हैं। फिर शायद कुछ अन्य चिप विशिष्ट सामान किया जाता है, या हम बाकी चिप विशिष्ट सामान को सी में करते हैं।

हाथ से कॉर्टेक्स-एम सीरीज़ कोर आपके लिए यह कुछ करेगा, स्टैक पॉइंटर वेक्टर टेबल में है, रीसेट के बाद चलाने के लिए कोड पर इंगित करने के लिए एक रीसेट वेक्टर है, ताकि आपको जो करना है उसके अलावा अन्य वेक्टर टेबल जेनरेट करने के लिए (जिसका उपयोग आप आमतौर पर किसी भी तरह से करते हैं) आप बिना एस के शुद्ध सी जा सकते हैं। अब आप अपने .data को न तो कॉपी करें और न ही अपने .bs को शून्य करें ताकि आपको स्वयं ऐसा करना पड़े यदि आप कुछ कॉर्टेक्स-मी पर आधारित एएसएम के बिना जाने की कोशिश करना चाहते हैं। बड़ी सुविधा रीसेट वेक्टर नहीं बल्कि इंटरप्ट वैक्टर है जहां हार्डवेयर हथियारों की सिफारिश करते हुए सी कॉलिंग कन्वेंशन की सिफारिश करता है और आपके लिए रजिस्टरों को संरक्षित करता है, और उस वेक्टर के लिए सही रिटर्न का उपयोग करता है, ताकि आपको प्रत्येक हैंडलर के चारों ओर सही एएसएम लपेटना न पड़े ( या आपके लक्ष्य के लिए टूलचिन के विशिष्ट निर्देश हैं जो आपके लिए टूलचिन को इसे लपेटें)।

उदाहरण के लिए चिप विशिष्ट सामान हो सकता है, माइक्रोकंट्रोलर का उपयोग अक्सर बैटरी आधारित प्रणालियों में किया जाता है, इसलिए कम शक्ति होती है, इसलिए अधिकांश में से अधिकांश बाह्य उपकरणों के साथ रीसेट से बाहर आ जाते हैं, और आपको इनमें से प्रत्येक उप सिस्टम को चालू करना होगा ताकि आप उनका उपयोग कर सकें । Uarts, gpios, आदि अक्सर एक कम-ईश घड़ी की गति का उपयोग किया जाता है, सीधे एक क्रिस्टल या आंतरिक थरथरानवाला से। और आपका सिस्टम डिज़ाइन यह दिखा सकता है कि आपको एक तेज़ घड़ी की आवश्यकता है, इसलिए आप इसे आरंभ करते हैं। आपकी घड़ी फ़्लैश या रैम के लिए बहुत तेज़ हो सकती है इसलिए आपको घड़ी को ऊपर करने से पहले प्रतीक्षा की स्थिति बदलने की आवश्यकता हो सकती है। Uart, या usb या अन्य इंटरफेस को सेटअप करने की आवश्यकता हो सकती है। तब आपका आवेदन अपनी बात कर सकता है।

एक कंप्यूटर डेस्कटॉप, लैपटॉप, सर्वर और एक माइक्रोकंट्रोलर अलग नहीं हैं कि वे बूट / काम कैसे करते हैं। सिवाय इसके कि वे ज्यादातर एक चिप पर नहीं हैं। बायोस प्रोग्राम अक्सर सीपीयू से एक अलग चिप फ्लैश / रोम पर होता है। हालांकि हाल ही में x86 cpus अधिक से अधिक खींच रहा है जो एक ही पैकेज (pcie नियंत्रकों, आदि) में चिप्स का समर्थन करते थे, लेकिन आपके पास अभी भी आपके अधिकांश रैम और रोम ऑफ चिप हैं, लेकिन यह अभी भी एक प्रणाली है और यह अभी भी ठीक से काम करता है उच्च स्तर पर समान। सीपीयू बूट प्रक्रिया को अच्छी तरह से जाना जाता है, बोर्ड डिजाइनर फ़्लैश / रोम को पता स्थान में रखते हैं जहां सीपीयू बूट होता है। वह प्रोग्राम (एक x86 पीसी पर BIOS का हिस्सा) उपरोक्त सभी चीजों को करता है, यह विभिन्न बाह्य उपकरणों को शुरू करता है, यह नाटक को आरम्भ करता है, पीसीआई बसों की गणना करता है, और इसी तरह। अक्सर उपयोगकर्ता द्वारा बायोस सेटिंग्स या जिसे हम cmos सेटिंग्स कहते थे, के आधार पर काफी विन्यास योग्य है, क्योंकि उस समय जो तकनीक का उपयोग किया गया था। कोई बात नहीं, उपयोगकर्ता सेटिंग्स हैं जो आप जा सकते हैं और बायोस बूट कोड को बताने के लिए बदल सकते हैं कि यह कैसे बदलता है।

विभिन्न लोग विभिन्न शब्दावली का उपयोग करेंगे। एक चिप बूट, यह पहला कोड है जो चलता है। जिसे कभी-कभी बूटस्ट्रैप भी कहा जाता है। लोडर शब्द के साथ एक बूटलोडर का अर्थ अक्सर यह होता है कि यदि आप हस्तक्षेप करने के लिए कुछ नहीं करते हैं तो यह एक बूटस्ट्रैप है जो आपको सामान्य बूटिंग से कुछ बड़े, आपके आवेदन या ऑपरेटिंग सिस्टम में ले जाता है। लेकिन लोडर भाग का अर्थ है कि आप बूट प्रक्रिया को बाधित कर सकते हैं और फिर अन्य परीक्षण कार्यक्रमों को लोड कर सकते हैं। यदि आपने कभी भी एक अंतर्निहित लिनक्स सिस्टम पर उदाहरण के लिए uboot का उपयोग किया है, तो आप एक कुंजी को हिट कर सकते हैं और सामान्य बूट को रोक सकते हैं, फिर आप रैम में एक टेस्ट कर्नेल डाउनलोड कर सकते हैं और इसे फ्लैश के बजाय बूट कर सकते हैं, या आप अपना डाउनलोड कर सकते हैं खुद के कार्यक्रम, या आप नए कर्नेल को डाउनलोड कर सकते हैं, फिर बूटलोडर को इसे फ्लैश करने के लिए लिखें ताकि अगली बार जब आप बूट करें तो यह नया सामान चलाए।

जहाँ तक स्वयं सीपीयू, कोर प्रोसेसर, जो बाह्य उपकरणों से फ्लैश से रैम को नहीं जानता है। बूटलोडर, ऑपरेटिंग सिस्टम, एप्लिकेशन की कोई धारणा नहीं है। यह केवल निर्देशों का एक अनुक्रम है जिसे cpu में खिलाया जाता है। विभिन्न प्रोग्रामिंग कार्यों को एक दूसरे से अलग करने के लिए ये सॉफ्टवेयर शब्द हैं। एक दूसरे से सॉफ्टवेयर अवधारणाओं।

कुछ माइक्रोकंट्रोलर में एक अलग फ्लैश या फ्लैश के अलग क्षेत्र में चिप विक्रेता द्वारा प्रदान किया गया एक अलग बूट लोडर होता है जिसे आप संशोधित करने में सक्षम नहीं हो सकते हैं। इस मामले में अक्सर पिन या पिन का सेट होता है (मैं उन्हें पट्टियाँ कहता हूं) कि यदि आप रीसेट जारी होने से पहले उन्हें उच्च या निम्न टाई करते हैं तो आप तर्क और / या उस बूटलोडर को बता रहे हैं कि क्या करना है, उदाहरण के लिए एक पट्टा संयोजन हो सकता है उस बूटलोडर को चलाने के लिए चिप को बताएं और डेटा को फ्लैश में प्रोग्राम किए जाने के लिए uart पर प्रतीक्षा करें। पट्टियों को दूसरे तरीके से सेट करें और आपके प्रोग्राम बूट चिप विक्रेता बूटलोडर को नहीं, चिप की फील्ड प्रोग्रामिंग या आपके प्रोग्राम के दुर्घटनाग्रस्त होने से उबरने की अनुमति देते हैं। कभी-कभी यह सिर्फ शुद्ध तर्क है जो आपको फ्लैश को प्रोग्राम करने की अनुमति देता है। यह इन दिनों काफी आम है,

अधिकांश माइक्रोकंट्रोलर्स के पास राम की तुलना में अधिक फ्लैश है, इसका कारण यह है कि प्राथमिक उपयोग का मामला प्रोग्राम को सीधे फ्लैश से चलाने के लिए है, और केवल स्टैक और चर को कवर करने के लिए पर्याप्त रैम है। यद्यपि कुछ मामलों में आप रैम से प्रोग्राम चला सकते हैं, जिसे आपको राइट संकलित करना होगा और फ्लैश में स्टोर करना होगा, फिर कॉल करने से पहले कॉपी करें।

संपादित करें

flash.s

.cpu cortex-m0
.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang

.thumb_func
reset:
    bl notmain
    b hang

.thumb_func
hang:   b .

notmain.c

int notmain ( void )
{
    unsigned int x=1;
    unsigned int y;
    y = x + 1;

    return(0);
}

flash.ld

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > bob
    .rodata : { *(.rodata*) } > bob
    .bss : { *(.bss*) } > ted
    .data : { *(.bss*) } > ted AT > bob
}

तो यह एक कोर्टेक्स- m0 के लिए एक उदाहरण है, कोर्टेक्स-एमएस सभी एक ही काम करते हैं जहां तक ​​यह उदाहरण जाता है। इस उदाहरण के लिए, विशेष चिप में, हाथ के पते के स्थान में 0x00000000 पर पता फ्लैश है और 0x20000000 पर रैम है।

जिस तरह से 0x0000 पर एड्रेस में 32 बिट का कॉर्टेक्स-एम बूट है वह स्टैक पॉइंटर को इनिशियलाइज़ करने के लिए एड्रेस है। मुझे इस उदाहरण के लिए बहुत अधिक स्टैक की आवश्यकता नहीं है, इसलिए 0x20001000 पर्याप्त होगा, जाहिर है कि उस पते के नीचे राम होना चाहिए (जिस तरह से हाथ को धक्का दिया जाता है, क्या यह पहले घटाया जाता है, फिर धक्का देता है यदि आप 0x20001000 सेट करते हैं तो स्टैक पर पहला आइटम पता 0x2000FFFC पर है आपको 0x2000FFFC का उपयोग नहीं करना है)। 0x0004 पते पर 32 बिट शब्द रीसेट हैंडलर का पता है, मूल रूप से पहला कोड जो रीसेट के बाद चलता है। फिर अधिक कॉन्ट्रैक्ट और ईवेंट हैंडलर हैं जो कि कॉर्टेक्स एम कोर और चिप के लिए विशिष्ट हैं, संभवतः 128 या 256 के रूप में, यदि आप उनका उपयोग नहीं करते हैं, तो आपको उनके लिए तालिका सेटअप करने की आवश्यकता नहीं है, मैं प्रदर्शन के लिए कुछ में फेंक दिया प्रयोजनों।

मुझे इस उदाहरण में .data और न ही .bs से निपटने की आवश्यकता नहीं है क्योंकि मुझे पता है कि कोड को देखकर उन खंडों में कुछ भी नहीं है। अगर वहाँ मैं इसके साथ सौदा होगा, और एक दूसरे में होगा।

तो स्टैक सेटअप है, चेक, .data ने ध्यान रखा, चेक, .bs, चेक, इसलिए सी बूटस्ट्रैप सामान किया जाता है, सी के लिए प्रवेश समारोह के लिए शाखा कर सकता है क्योंकि कुछ कंपाइलर अतिरिक्त जंक जोड़ देंगे यदि वे फ़ंक्शन देखते हैं। main () और main के रास्ते पर, मैं उस सटीक नाम का उपयोग नहीं करता, मैंने अपने C एंट्री पॉइंट के रूप में यहाँ notmain () का उपयोग किया। तो रिसेट हैंडलर notmain () को कॉल करता है, अगर if / notmain () रिटर्न देता है तो यह हैंग हो जाता है जो कि एक अनंत लूप है, संभवतः खराब नाम वाला।

मैं दृढ़ता से उपकरणों में महारत हासिल करने में विश्वास करता हूं, बहुत से लोग नहीं करते हैं, लेकिन आप जो पाएंगे वह यह है कि प्रत्येक नंगे धातु डेवलपर अपने काम खुद करता है, क्योंकि पूर्ण स्वतंत्रता के निकट, दूर से संकुचित नहीं है क्योंकि आप एप्लिकेशन या वेब पेज बना रहे होंगे। । वे फिर से अपना काम करते हैं। मैं अपना बूटस्ट्रैप कोड और लिंकर स्क्रिप्ट रखना पसंद करता हूं। अन्य लोग टूलचेन पर भरोसा करते हैं, या विक्रेताओं के सैंडबॉक्स में खेलते हैं जहां अधिकांश काम किसी और द्वारा किया जाता है (और अगर कुछ टूटता है तो आप चोट की दुनिया में हैं, और नंगे धातु की चीजों के साथ अक्सर और नाटकीय तरीके से टूटते हैं)।

इसलिए मुझे मिलने वाले गन टूल के साथ संयोजन, संकलन और लिंकिंग:

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000001b    andeq   r0, r0, fp, lsl r0
   c:   0000001b    andeq   r0, r0, fp, lsl r0
  10:   0000001b    andeq   r0, r0, fp, lsl r0

00000014 <reset>:
  14:   f000 f802   bl  1c <notmain>
  18:   e7ff        b.n 1a <hang>

0000001a <hang>:
  1a:   e7fe        b.n 1a <hang>

0000001c <notmain>:
  1c:   2000        movs    r0, #0
  1e:   4770        bx  lr

तो बूटलोडर कैसे जानता है कि सामान कहाँ है। क्योंकि कंपाइलर ने काम किया। पहले मामले में कोडांतरक ने फ़्लैश.s के लिए कोड उत्पन्न किया, और ऐसा करने से पता चलता है कि लेबल कहां हैं (लेबल केवल फ़ंक्शन नाम या परिवर्तनशील नाम, आदि जैसे पते हैं) इसलिए मुझे बाइट गिनना नहीं है और वेक्टर में भरना है तालिका मैन्युअल रूप से, मैंने एक लेबल नाम का उपयोग किया और कोडांतरक ने मेरे लिए किया। अब आप पूछते हैं कि यदि रीसेट 0x14 पता है तो कोडांतरक ने वेक्टर तालिका में 0x15 क्यों डाला। वैसे यह एक कोर्टेक्स-एम है और यह बूट होता है और केवल अंगूठे मोड में चलता है। एआरएम के साथ जब आप एक पते पर शाखा करते हैं यदि शाखा मोड में अंगूठे को सेट करने की आवश्यकता होती है, तो आर्म मोड रीसेट हो जाता है। इसलिए आपको हमेशा उस बिट सेट की आवश्यकता होती है। मुझे उपकरण पता है और एक लेबल से पहले .thumb_func डालकर, अगर उस लेबल का उपयोग किया जाता है जैसा कि वह वेक्टर टेबल में या ब्रांचिंग के लिए या जो भी हो। टूलकिन को लैबिट सेट करना जानता है। तो यह यहाँ 0x14 | 1 = 0x15 है। इसी तरह फांसी के लिए भी। अब disassembler नॉटमैन () को कॉल करने के लिए 0x1D शो नहीं करता है, लेकिन चिंता न करें कि टूल ने निर्देश को सही तरीके से बनाया है।

अब उस कोड में नोटमैन, जो स्थानीय चर हैं, उपयोग नहीं किए गए हैं, वे मृत कोड हैं। कंपाइलर उस तथ्य पर भी टिप्पणी करता है जिसमें y सेट किया गया है लेकिन उपयोग नहीं किया गया है।

एड्रेस स्पेस पर ध्यान दें, ये सभी चीजें एड्रेस 0x0000 से शुरू होती हैं और वहां से जाती हैं, इसलिए वेक्टर टेबल को ठीक से रखा गया है, इनटेक्स या प्रोग्राम स्पेस को भी ठीक से रखा गया है, मुझे कैसे पता चला कि मैं notmain.c के कोड के सामने फ़्लैश हूं। उपकरणों को जानना, एक सामान्य गलती यह है कि सही और दुर्घटनाग्रस्त न हो और कड़ी मेहनत से जलाएं। IMO आपको यह सुनिश्चित करने के लिए अलग करना होगा कि पहली बार बूट करने से पहले चीजें सही रखी गई हैं, एक बार जब आपके पास सही जगह की चीजें हैं, तो आपको हर बार जांचना जरूरी नहीं है। बस नई परियोजनाओं के लिए या यदि वे लटकाते हैं।

अब कुछ लोग जो कुछ लोगों को आश्चर्यचकित करते हैं, वह यह है कि किसी भी दो संकलक से एक ही इनपुट से एक ही आउटपुट का निर्माण करने की अपेक्षा करने का कोई कारण नहीं है। या विभिन्न सेटिंग्स के साथ भी एक ही संकलक। क्लैंग का उपयोग करते हुए, llvm संकलक मैं इन दो आउटपुट को अनुकूलन के साथ और बिना प्राप्त करता हूं

llvm / clang अनुकूलित

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000001b    andeq   r0, r0, fp, lsl r0
   c:   0000001b    andeq   r0, r0, fp, lsl r0
  10:   0000001b    andeq   r0, r0, fp, lsl r0

00000014 <reset>:
  14:   f000 f802   bl  1c <notmain>
  18:   e7ff        b.n 1a <hang>

0000001a <hang>:
  1a:   e7fe        b.n 1a <hang>

0000001c <notmain>:
  1c:   2000        movs    r0, #0
  1e:   4770        bx  lr

अनुकूलित नहीं है

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000001b    andeq   r0, r0, fp, lsl r0
   c:   0000001b    andeq   r0, r0, fp, lsl r0
  10:   0000001b    andeq   r0, r0, fp, lsl r0

00000014 <reset>:
  14:   f000 f802   bl  1c <notmain>
  18:   e7ff        b.n 1a <hang>

0000001a <hang>:
  1a:   e7fe        b.n 1a <hang>

0000001c <notmain>:
  1c:   b082        sub sp, #8
  1e:   2001        movs    r0, #1
  20:   9001        str r0, [sp, #4]
  22:   2002        movs    r0, #2
  24:   9000        str r0, [sp, #0]
  26:   2000        movs    r0, #0
  28:   b002        add sp, #8
  2a:   4770        bx  lr

ऐसा इसलिए है कि एक कंपाइलर ने इसके अतिरिक्त अनुकूलन किया, लेकिन इसने चर के लिए स्टैक पर दो आइटम आवंटित किए, क्योंकि ये स्थानीय चर हैं जो वे राम में हैं लेकिन स्टैक पर निश्चित पते पर नहीं, ग्लोबल्स के साथ देखेंगे। परिवर्तन। लेकिन संकलक को एहसास हुआ कि यह संकलन समय पर y की गणना कर सकता है और इसे चलाने के समय में गणना करने का कोई कारण नहीं था, इसलिए इसने x के लिए आवंटित स्टैक स्पेस में 1 और y के लिए आवंटित स्टैक स्पेस के लिए 2 रखा। संकलक आंतरिक तालिकाओं के साथ इस स्थान को "आवंटित" करता है जिसे मैं वेरिएबल y के लिए स्टैक प्लस 0 और वेरिएबल x के लिए स्टैक प्लस 4 घोषित करता हूं। कंपाइलर जो कुछ भी करना चाहता है वह कर सकता है जब तक कि यह कोड लागू होता है वह सी प्रोग्रामर के सी मानक या एक्सपेक्टेशन के अनुरूप होता है। फ़ंक्शन की अवधि के लिए कंपाइलर को स्टैक + 4 पर छोड़ने का कोई कारण नहीं है,

अगर मैं असेंबलर में एक फ़ंक्शन डमी जोड़ता हूं

.thumb_func
.globl dummy
dummy:
    bx lr

और फिर इसे कॉल करें

void dummy ( unsigned int );
int notmain ( void )
{
    unsigned int x=1;
    unsigned int y;
    y = x + 1;
    dummy(y);
    return(0);
}

आउटपुट बदलता है

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000001b    andeq   r0, r0, fp, lsl r0
   c:   0000001b    andeq   r0, r0, fp, lsl r0
  10:   0000001b    andeq   r0, r0, fp, lsl r0

00000014 <reset>:
  14:   f000 f804   bl  20 <notmain>
  18:   e7ff        b.n 1a <hang>

0000001a <hang>:
  1a:   e7fe        b.n 1a <hang>

0000001c <dummy>:
  1c:   4770        bx  lr
    ...

00000020 <notmain>:
  20:   b510        push    {r4, lr}
  22:   2002        movs    r0, #2
  24:   f7ff fffa   bl  1c <dummy>
  28:   2000        movs    r0, #0
  2a:   bc10        pop {r4}
  2c:   bc02        pop {r1}
  2e:   4708        bx  r1

अब जब हमारे पास नेस्टेड फ़ंक्शन हैं, तो नोटमैन फ़ंक्शन को अपना रिटर्न पता संरक्षित करने की आवश्यकता है, ताकि यह नेस्टेड कॉल के लिए रिटर्न एड्रेस को क्लोब कर सके। इसका कारण यह है कि भुजा रिटर्न के लिए एक रजिस्टर का उपयोग करता है, अगर यह स्टैक का उपयोग करता है जैसे कि x86 या कुछ अन्य अच्छी तरह से कहें ... यह अभी भी स्टैक का उपयोग करेगा लेकिन अलग तरह से। अब आप पूछते हैं कि इसने r4 को धक्का क्यों दिया? ठीक है, बहुत पहले नहीं बुलाए गए सम्मेलन ने 32 बिट के बजाय 64 बिट (दो शब्द) सीमाओं पर संरेखित रखने के लिए बदल दिया, एक सीमाएं। इसलिए उन्हें स्टैक को संरेखित रखने के लिए कुछ धक्का देने की आवश्यकता है, इसलिए कंपाइलर ने मनमाने ढंग से किसी कारण से r4 को चुना, इससे कोई फर्क नहीं पड़ता। इस लक्ष्य के लिए बुलाए गए सम्मेलन के अनुसार, r4 में एक बग बग होगा, हम फंक्शन कॉल पर clobber r4 नहीं करते, हम r3 के माध्यम से r0 को clobber कर सकते हैं। r0 रिटर्न वैल्यू है। ऐसा लगता है कि शायद यह एक पूंछ अनुकूलन कर रहा है,

लेकिन हम देखते हैं कि x और y गणित डमी फ़ंक्शन में पारित होने के 2 के हार्डकोडेड मान के अनुकूल है (डमी को विशेष रूप से एक अलग फाइल में कोडित किया गया था, इस मामले में एएसएम, ताकि कंपाइलर फ़ंक्शन कॉल को पूरी तरह से ऑप्टिमाइज़ नहीं कर सके। अगर मेरे पास एक डमी फंक्शन होता है, जो केवल notmain.c में C में वापस आता है, तो ऑप्टिमाइज़र ने x, y और डमी फंक्शन कॉल को हटा दिया होगा क्योंकि वे सभी डेड / बेकार कोड हैं)।

यह भी ध्यान दें कि क्योंकि फ्लैश.एस कोड को बड़ा नोट मिल गया है, वह कोई और नहीं है और टूलचिन ने हमारे लिए सभी पते को पैच करने का ध्यान रखा है इसलिए हमें मैन्युअल रूप से ऐसा नहीं करना है।

संदर्भ के लिए अडॉप्टेड क्लैंग

00000020 <notmain>:
  20:   b580        push    {r7, lr}
  22:   af00        add r7, sp, #0
  24:   b082        sub sp, #8
  26:   2001        movs    r0, #1
  28:   9001        str r0, [sp, #4]
  2a:   2002        movs    r0, #2
  2c:   9000        str r0, [sp, #0]
  2e:   f7ff fff5   bl  1c <dummy>
  32:   2000        movs    r0, #0
  34:   b002        add sp, #8
  36:   bd80        pop {r7, pc}

अनुकूलित क्लैंग

00000020 <notmain>:
  20:   b580        push    {r7, lr}
  22:   af00        add r7, sp, #0
  24:   2002        movs    r0, #2
  26:   f7ff fff9   bl  1c <dummy>
  2a:   2000        movs    r0, #0
  2c:   bd80        pop {r7, pc}

उस संकलक लेखक ने स्टैक को संरेखित करने के लिए डमी वैरिएबल के रूप में r7 का उपयोग करने के लिए चुना, यह भी r7 का उपयोग करके एक फ्रेम पॉइंटर बना रहा है, भले ही इसमें स्टैक फ्रेम में कुछ भी न हो। मूल रूप से निर्देश को अनुकूलित किया जा सकता था। लेकिन इसने तीन निर्देशों को वापस करने के लिए पॉप का उपयोग किया, जो शायद मुझ पर था मुझे यकीन था कि मैं इसे सही कमांड लाइन विकल्पों (प्रोसेसर को निर्दिष्ट करने) के साथ कर सकता हूं।

यह ज्यादातर आपके सवालों के जवाब देना चाहिए

void dummy ( unsigned int );
unsigned int x=1;
unsigned int y;
int notmain ( void )
{
    y = x + 1;
    dummy(y);
    return(0);
}

मेरे पास अब ग्लोबल्स हैं। इसलिए वे या तो .data या .bs में जाते हैं अगर वे बाहर अनुकूलित नहीं मिलते हैं।

इससे पहले कि हम अंतिम आउटपुट को देखें इटर्माडिएट ऑब्जेक्ट को देखें

00000000 <notmain>:
   0:   b510        push    {r4, lr}
   2:   4b05        ldr r3, [pc, #20]   ; (18 <notmain+0x18>)
   4:   6818        ldr r0, [r3, #0]
   6:   4b05        ldr r3, [pc, #20]   ; (1c <notmain+0x1c>)
   8:   3001        adds    r0, #1
   a:   6018        str r0, [r3, #0]
   c:   f7ff fffe   bl  0 <dummy>
  10:   2000        movs    r0, #0
  12:   bc10        pop {r4}
  14:   bc02        pop {r1}
  16:   4708        bx  r1
    ...

Disassembly of section .data:
00000000 <x>:
   0:   00000001    andeq   r0, r0, r1

अब इसमें से जानकारी गायब है, लेकिन यह इस बात का अंदाजा लगाता है कि क्या चल रहा है, लिंकर वह है जो वस्तुओं को ले जाता है और उन्हें सूचना के साथ एक साथ जोड़ता है (बशर्ते कि इस मामले में Flash.ld) यह बताता है कि .text और। डेटा और ऐसे जाता है। संकलक ऐसी चीजों को नहीं जानता है, यह केवल उस कोड पर ध्यान केंद्रित कर सकता है जो इसे प्रस्तुत किया गया है, किसी भी बाहरी इसे लिंकर को कनेक्शन में भरने के लिए एक छेद छोड़ना होगा। किसी भी डेटा को उन चीजों को एक साथ जोड़ने का एक तरीका छोड़ना पड़ता है, इसलिए सब कुछ के लिए पते यहां शून्य आधारित हैं, क्योंकि संकलक और इस असंतुष्ट व्यक्ति को नहीं पता है। अन्य जानकारी यहाँ नहीं दिखाई गई है कि लिंकर चीजों को रखने के लिए उपयोग करता है। यहां कोड पर्याप्त स्वतंत्र स्थिति है, ताकि लिंकर अपना काम कर सके।

फिर हम कम से कम लिंक किए गए आउटपुट के डिस्सैस को देखते हैं

00000020 <notmain>:
  20:   b510        push    {r4, lr}
  22:   4b05        ldr r3, [pc, #20]   ; (38 <notmain+0x18>)
  24:   6818        ldr r0, [r3, #0]
  26:   4b05        ldr r3, [pc, #20]   ; (3c <notmain+0x1c>)
  28:   3001        adds    r0, #1
  2a:   6018        str r0, [r3, #0]
  2c:   f7ff fff6   bl  1c <dummy>
  30:   2000        movs    r0, #0
  32:   bc10        pop {r4}
  34:   bc02        pop {r1}
  36:   4708        bx  r1
  38:   20000004    andcs   r0, r0, r4
  3c:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <y>:
20000000:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

20000004 <x>:
20000004:   00000001    andeq   r0, r0, r1

संकलक ने मूल रूप से रैम में दो 32 बिट चर के लिए कहा है। एक .bs में है क्योंकि मैंने इसे शुरू नहीं किया है इसलिए इसे शून्य के रूप में माना जाता है। दूसरा है .डेटा क्योंकि मैंने घोषणा पर इसे इनिशियलाइज़ किया था।

अब क्योंकि ये वैश्विक चर हैं इसलिए यह माना जाता है कि अन्य कार्य इन्हें संशोधित कर सकते हैं। संकलक कोई धारणा नहीं बनाता है कि कब नोटिन कहा जा सकता है इसलिए यह जो इसे देख सकता है उसके साथ अनुकूलन नहीं कर सकता है, y = x + 1 गणित, इसलिए इसे उस रनटाइम को करना होगा। यह राम से पढ़ना है दो चर उन्हें जोड़ते हैं और वापस बचाते हैं।

अब स्पष्ट रूप से यह कोड काम नहीं करेगा। क्यों? क्योंकि मेरा बूटस्ट्रैप जैसा कि यहां दिखाया गया है, नोटमैन को बुलाने से पहले राम को तैयार नहीं करता है, इसलिए जो भी कचरा 0x20000000 और 0x20000004 में था, जब चिप जगाया जाता है, तो इसका उपयोग y और x के लिए किया जाएगा।

यहां नहीं दिखाने जा रहे हैं। आप मेरे लंबे समय तक घुमावदार डेडटा और .bss पर पढ़ सकते हैं और मुझे अपने नंगे धातु कोड में कभी भी उनकी आवश्यकता क्यों नहीं है, लेकिन अगर आपको लगता है कि आपको किसी और की उम्मीद करने के बजाय उपकरण को मास्टर करना है और इसे सही करना है .. ।

https://github.com/dwelch67/raspberrypi/tree/master/bssdata

लिंकर स्क्रिप्ट, और बूटस्ट्रैप कुछ विशिष्ट संकलक हैं, इसलिए जब आप एक संकलक के एक संस्करण के बारे में सीखते हैं, तो अगले संस्करण पर या किसी अन्य संकलक के साथ फेंक दिया जा सकता है, फिर भी एक और कारण है कि मैं एक टन प्रयास में नहीं डाल सकता हूँ .data और .bs तैयारी। बस इस आलसी होने के लिए:

unsigned int x=1;

मैं ऐसा नहीं करूंगा

unsigned int x;
...
x = 1;

और कंपाइलर ने इसे मेरे लिए .text में डाल दिया। कभी-कभी यह फ्लैश को बचाता है जिस तरह से कभी-कभी यह अधिक जलता है। यह टूलचैन संस्करण या एक संकलक से दूसरे में प्रोग्राम और पोर्ट के लिए सबसे निश्चित रूप से बहुत आसान है। बहुत अधिक विश्वसनीय, कम त्रुटि प्रवण। हां, सी मानक के अनुरूप नहीं है।

अब क्या होगा अगर हम इन स्थैतिक ग्लोबल्स को बनाते हैं?

void dummy ( unsigned int );
static unsigned int x=1;
static unsigned int y;
int notmain ( void )
{
    y = x + 1;
    dummy(y);
    return(0);
}

कुंआ

00000020 <notmain>:
  20:   b510        push    {r4, lr}
  22:   2002        movs    r0, #2
  24:   f7ff fffa   bl  1c <dummy>
  28:   2000        movs    r0, #0
  2a:   bc10        pop {r4}
  2c:   bc02        pop {r1}
  2e:   4708        bx  r1

स्पष्ट रूप से उन चर को अन्य कोड द्वारा संशोधित नहीं किया जा सकता है, इसलिए संकलक अब संकलित समय पर मृत कोड का अनुकूलन कर सकता है, जैसे कि यह पहले किया था।

unoptimized

00000020 <notmain>:
  20:   b580        push    {r7, lr}
  22:   af00        add r7, sp, #0
  24:   4804        ldr r0, [pc, #16]   ; (38 <notmain+0x18>)
  26:   6800        ldr r0, [r0, #0]
  28:   1c40        adds    r0, r0, #1
  2a:   4904        ldr r1, [pc, #16]   ; (3c <notmain+0x1c>)
  2c:   6008        str r0, [r1, #0]
  2e:   f7ff fff5   bl  1c <dummy>
  32:   2000        movs    r0, #0
  34:   bd80        pop {r7, pc}
  36:   46c0        nop         ; (mov r8, r8)
  38:   20000004    andcs   r0, r0, r4
  3c:   20000000    andcs   r0, r0, r0

यह संकलक जो स्थानीय लोगों के लिए स्टैक का उपयोग करता था, अब ग्लोबल्स के लिए रैम का उपयोग करता है और लिखित रूप में यह कोड टूट गया है क्योंकि मैंने नहीं किया है।

और एक आखिरी चीज जो हम कठबोली में देख सकते हैं।

:1000000000100020150000001B0000001B00000075
:100010001B00000000F004F8FFE7FEE77047000057
:1000200080B500AF04480068401C04490860FFF731
:10003000F5FF002080BDC046040000200000002025
:08004000E0FFFF7F010000005A
:0400480078563412A0
:00000001FF

मैंने x को 0x12345678 के साथ प्री-इनिट किया। मेरी लिंकर स्क्रिप्ट (यह gnu ld के लिए है) में bob चीज़ पर यह टेड है। लिंक करने वाले को बताता है कि मैं चाहता हूं कि अंतिम स्थान टेड एड्रेस स्पेस में हो, लेकिन इसे टेड एड्रेस स्पेस में बाइनरी में स्टोर करें और कोई इसे आपके लिए स्थानांतरित कर देगा। और हम देख सकते हैं कि हुआ। यह इंटेल हेक्स प्रारूप है। और हम 0x12345678 देख सकते हैं

:0400480078563412A0

बाइनरी के फ्लैश एड्रेस स्पेस में है।

यह भी पता चलता है

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  EXIDX          0x010040 0x00000040 0x00000040 0x00008 0x00008 R   0x4
  LOAD           0x010000 0x00000000 0x00000000 0x00048 0x00048 R E 0x10000
  LOAD           0x020004 0x20000004 0x00000048 0x00004 0x00004 RW  0x10000
  LOAD           0x030000 0x20000000 0x20000000 0x00000 0x00004 RW  0x10000
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10

LOAD लाइन जहां आभासी पता 0x20000004 है और भौतिक 0x48 है


शुरुआत में मेरे पास चीजों की दो धुंधली तस्वीर है:
user16307

1.) "प्राथमिक उपयोग के मामले में रोम में निर्देश / फ्लैश और डेटा है।" जब आप "RAM में डेटा यहाँ" कहते हैं, तो क्या आपका मतलब कार्यक्रम की सफलता में निहित डेटा है। या क्या आप आरंभिक डेटा भी शामिल करते हैं। मेरा मतलब है कि जब हम कोड को ROM पर अपलोड करते हैं, तो हमारे कोड में पहले से ही आरंभिक डेटा होता है। हमारे ओड में उदाहरण के लिए यदि हमारे पास है: int x = 1; int y = x +1; उपरोक्त कोड में निर्देश हैं और एक प्रारंभिक डेटा है जो 1. (x = 1) है। क्या यह डेटा भी RAM में कॉपी किया गया है या केवल ROM में रहता है।
user16307

13
हह, मुझे अब स्टैक एक्सचेंज उत्तर के लिए वर्ण सीमा पता है!
ओल्ड_टाइमर

3
यू को इस तरह की अवधारणाओं को समझाते हुए एक किताब लिखनी चाहिए। "मेरे पास
गितुब में

1
मैंने अभी किया। ऐसा नहीं है जो कुछ भी उपयोगी है, लेकिन फिर भी यह एक माइक्रोकंट्रोलर के लिए कोड का एक उदाहरण है। और मैंने एक जीथब लिंक डाला, जिसमें से आप वह सब कुछ पा सकते हैं जो मैंने साझा किया है, अच्छा, बुरा, या अन्यथा।
Old_timer

8

यह उत्तर बूट प्रक्रिया पर अधिक ध्यान केंद्रित करने वाला है। सबसे पहले, एक सुधार - फ्लैश करने के लिए लिखता है एमसीयू (या इसके कम से कम भाग) के बाद किया जाता है। कुछ MCU (आमतौर पर अधिक उन्नत वाले) पर, CPU स्वयं सीरियल पोर्ट को संचालित कर सकता है और फ़्लैश रजिस्टरों को लिख सकता है। इसलिए कार्यक्रम को लिखना और निष्पादित करना अलग-अलग प्रक्रियाएं हैं। मैं मान रहा हूं कि कार्यक्रम को फ्लैश करने के लिए पहले ही लिखा जा चुका है।

यहां मूल बूट प्रक्रिया है। मैं कुछ सामान्य विविधताओं का नाम लूंगा, लेकिन ज्यादातर मैं इसे सरल रख रहा हूं।

  1. रीसेट: दो बुनियादी प्रकार हैं। पहला एक पावर-ऑन रीसेट है, जो आंतरिक रूप से उत्पन्न होता है जबकि आपूर्ति वोल्टेज ऊपर रैंप पर होता है। दूसरा एक बाहरी पिन टॉगल है। भले ही, रीसेट सभी MCU में फ्लिप-फ्लॉप को पूर्व निर्धारित स्थिति के लिए मजबूर करता है।

  2. अतिरिक्त हार्डवेयर आरंभीकरण: सीपीयू के चलने से पहले अतिरिक्त समय और / या घड़ी चक्र की आवश्यकता हो सकती है। उदाहरण के लिए, मैं जिस TI MCUs पर काम करता हूं, उसमें एक आंतरिक कॉन्फ़िगरेशन स्कैन श्रृंखला है जो लोड होती है।

  3. CPU बूट: CPU अपना पहला निर्देश एक विशेष पते से प्राप्त करता है जिसे रीसेट वेक्टर कहा जाता है यह पता तब निर्धारित किया जाता है जब सीपीयू डिज़ाइन किया जाता है। वहां से, यह सिर्फ सामान्य कार्यक्रम निष्पादन है।

    सीपीयू बार-बार तीन बुनियादी चरणों को दोहराता है:

    • Fetch: प्रोग्राम काउंटर (पीसी) रजिस्टर में संग्रहीत पते से एक निर्देश (8-, 16-, या 32-बिट मान) पढ़ें , फिर पीसी बढ़ाएँ।
    • डिकोड: सीपीयू के आंतरिक नियंत्रण संकेतों के लिए द्विआधारी निर्देश को मूल्य के एक सेट में परिवर्तित करें।
    • निष्पादित करें: निर्देश को ले जाएं - दो रजिस्टर जोड़ें, स्मृति से पढ़ें या लिखें, शाखा (पीसी बदलें) या जो भी हो।

    (यह वास्तव में इस से अधिक जटिल है। सीपीयू आमतौर पर पाइपलाइन किए जाते हैं , जिसका अर्थ है कि वे उपरोक्त चरणों में से प्रत्येक को एक ही समय में अलग-अलग निर्देशों पर कर सकते हैं। ऊपर दिए गए प्रत्येक चरण में कई पाइपलाइन चरण हो सकते हैं। इसके बाद समानांतर पाइपलाइन, शाखा भविष्यवाणी है। , और सभी फैंसी कंप्यूटर आर्किटेक्चर सामान जो उन इंटेल सीपीयू को डिजाइन करने के लिए एक अरब ट्रांजिस्टर लेते हैं। "

    आप सोच रहे होंगे कि यह कैसे काम करता है। सीपीयू के पास एक बस है होती है जिसमें पता (आउट) और डेटा (इन / आउट) सिग्नल होते हैं। एक भ्रूण लाने के लिए, सीपीयू प्रोग्राम काउंटर में मूल्य के लिए अपनी पता लाइनें सेट करता है, फिर बस पर एक घड़ी भेजता है। स्मृति को सक्षम करने के लिए पता डीकोड किया गया है। मेमोरी घड़ी और पता प्राप्त करती है, और डेटा लाइनों पर उस पते पर मूल्य डालती है। CPU को यह मान प्राप्त होता है। डेटा पढ़ना और लिखना समान है, सिवाय पते के निर्देश या सामान्य-उद्देश्य रजिस्टर में एक मूल्य से आता है, न कि पीसी।

    वॉन न्यूमैन आर्किटेक्चर के साथ सीपीयू के पास एक एकल बस है जो निर्देशों और डेटा दोनों के लिए उपयोग की जाती है। हार्वर्ड आर्किटेक्चर वाले सीपीयू में निर्देशों के लिए एक बस और डेटा के लिए एक बस है। वास्तविक MCUs में, ये दोनों बसें एक ही यादों से जुड़ी हो सकती हैं, इसलिए यह अक्सर (लेकिन हमेशा नहीं) कुछ है जिसके बारे में आपको चिंता करने की ज़रूरत नहीं है।

    वापस बूट प्रक्रिया के लिए। रीसेट के बाद, पीसी को एक शुरुआती मान के साथ लोड किया जाता है जिसे रीसेट वेक्टर कहा जाता है यह हार्डवेयर में बनाया जा सकता है, या (एआरएम कोर्टेक्स-एम सीपीयू में) इसे मेमोरी से बाहर स्वचालित रूप से पढ़ा जा सकता है। सीपीयू रीसेट वेक्टर से निर्देश प्राप्त करता है और ऊपर दिए गए चरणों के माध्यम से लूपिंग शुरू करता है। इस बिंदु पर, सीपीयू सामान्य रूप से निष्पादित हो रहा है।

  4. बूट लोडर: अक्सर कुछ निम्न-स्तरीय सेटअप होते हैं, जिन्हें एमसीयू के बाकी हिस्सों को चालू करने के लिए किया जाना चाहिए। इसमें रैम समाशोधन और एनालॉग घटकों के लिए विनिर्माण ट्रिम सेटिंग्स लोड करने जैसी चीजें शामिल हो सकती हैं। बाहरी स्रोत से कोड लोड करने का विकल्प भी हो सकता है जैसे कि सीरियल पोर्ट या बाहरी मेमोरी। MCU में एक बूट ROM शामिल हो सकता है जिसमें इन चीजों को करने के लिए एक छोटा प्रोग्राम होता है। इस स्थिति में, सीपीयू वेक्टर को बूट ROM के एड्रेस स्पेस में रीसेट कर देता है। यह मूल रूप से सामान्य कोड है, यह सिर्फ निर्माता द्वारा प्रदान किया जाता है, इसलिए आपको इसे स्वयं लिखने की आवश्यकता नहीं है। :-) एक पीसी में, BIOS बूट ROM के बराबर है।

  5. C पर्यावरण सेटअप: C को स्टैक (फ़ंक्शन कॉल के दौरान राज्य के भंडारण के लिए रैम क्षेत्र) और वैश्विक चर के लिए आरंभिक मेमोरी स्थानों की उम्मीद है। ये .stack, .data और .bss सेक्शन हैं जिनके बारे में ड्वेलच बात कर रहे हैं। आरंभिक वैश्विक चरों में इस चरण में फ्लैश से रैम तक कॉपी किए गए उनके आरंभिक मूल्य हैं। Uninitialized वैश्विक वैरिएबल में RAM पते होते हैं जो एक साथ पास होते हैं, इसलिए मेमोरी के पूरे ब्लॉक को बहुत आसानी से शून्य से आरंभ किया जा सकता है। स्टैक को इनिशियलाइज़ करने की आवश्यकता नहीं है (हालाँकि यह हो सकता है) - आप सभी को वास्तव में सीपीयू के स्टैक पॉइंटर रजिस्टर को सेट करने की आवश्यकता है ताकि यह रैम में दिए गए क्षेत्र को इंगित करे।

  6. मुख्य कार्य : एक बार C वातावरण सेट करने के बाद, C लोडर मुख्य () फ़ंक्शन को कॉल करता है। यहीं से आपका एप्लिकेशन कोड सामान्य रूप से शुरू होता है। यदि आप चाहें, तो आप मानक पुस्तकालय को छोड़ सकते हैं, सी पर्यावरण सेटअप को छोड़ सकते हैं, और मुख्य () को कॉल करने के लिए अपना कोड लिख सकते हैं। कुछ MCU आपको अपने बूट लोडर को लिखने की अनुमति दे सकते हैं, और फिर आप अपने स्तर पर सभी निम्न-स्तरीय सेटअप कर सकते हैं।

विविध सामान: कई MCU आपको बेहतर प्रदर्शन के लिए RAM से कोड निष्पादित करने देंगे। यह आमतौर पर लिंकर कॉन्फ़िगरेशन में स्थापित किया जाता है। लिंकर प्रत्येक फ़ंक्शन को दो पते प्रदान करता है - एक लोड एड्रेस , जो कि जहां कोड को पहले स्टोर किया जाता है (आमतौर पर फ्लैश), और एक रन एड्रेस , जो फ़ंक्शन (फ्लैश या रैम) को निष्पादित करने के लिए पीसी में लोड किया गया एड्रेस होता है। RAM से कोड को निष्पादित करने के लिए, आप CPU को उसके लोड पते से फ़ंक्शन कोड को कॉपी करने के लिए फ्लैश में रैम में इसके रन पते पर लिखते हैं, फिर रन पते पर फ़ंक्शन को कॉल करते हैं। लिंकर इसके साथ मदद करने के लिए वैश्विक चर को परिभाषित कर सकता है। लेकिन MCUs में RAM से कोड निष्पादित करना वैकल्पिक है। यदि आप वास्तव में उच्च प्रदर्शन की आवश्यकता है या यदि आप फ्लैश को फिर से लिखना चाहते हैं, तो आप सामान्य रूप से केवल यह करेंगे।


1

वॉन न्यूमैन वास्तुकला के लिए आपका सारांश लगभग सही है । प्रारंभिक कोड आमतौर पर एक बूटलोडर के माध्यम से रैम में लोड होता है, लेकिन (आमतौर पर) एक सॉफ्टवेयर बूटलोडर नहीं होता है जो आमतौर पर शब्द को संदर्भित करता है। यह आमतौर पर 'सिलिकॉन में पके हुए' व्यवहार है। इस आर्किटेक्चर में कोड निष्पादन में अक्सर रॉम से एक भविष्य कहनेवाला कैशिंग ओ निर्देश शामिल होता है, जिससे प्रोसेसर अपना समय निष्पादित कोड को अधिकतम करता है और कोड को रैम में लोड होने की प्रतीक्षा नहीं करता है। मैंने कहीं पढ़ा है कि MSP430 इस वास्तुकला का एक उदाहरण है।

एक में हार्वर्ड वास्तुकला डिवाइस, निर्देश, जबकि डेटा स्मृति (RAM) एक अलग बस के माध्यम से पहुँचा जा सकता है ROM से सीधे क्रियान्वित कर रहे हैं। इस आर्किटेक्चर में, कोड बस रीसेट वेक्टर से निष्पादित करना शुरू कर देता है। PIC24 और dsPIC33 इस वास्तुकला के उदाहरण हैं।

बिट्स के वास्तविक फ़्लिपिंग के लिए, जो इन प्रक्रियाओं को बंद कर देता है, जो डिवाइस-टू-डिवाइस से भिन्न हो सकता है और इसमें डिबगर, जेटीएजी, मालिकाना तरीके आदि शामिल हो सकते हैं।


लेकिन आप कुछ बिंदुओं को तेजी से छोड़ रहे हैं। इसे धीमी गति पर ले जाएं। मान लें कि बाइनरी कोड "पहला" ROM को लिखा गया है। ठीक है .. उसके बाद आप लिखते हैं "डेटा मेमोरी एक्सेस की गई है" .... लेकिन रैम "कहां डेटा" पहली बार स्टार्ट अप से आता है? क्या यह फिर से ROM से आता है? और यदि हां, तो बूट-लोडर को कैसे पता चलता है कि शुरुआत में RAM को ROM का कौन सा भाग लिखा जाएगा?
user16307

आप सही हैं, मैंने काफी हद तक स्किप किया। दूसरे लोगों के पास बेहतर जवाब हैं। मुझे खुशी है कि आपको वह मिल गया जिसकी आपको तलाश थी।
थोड़ा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.