एआरएम कोर्टेक्स-एम 4 माइक्रोकंट्रोलर के लिए ढेर और स्टैक आकार को परिभाषित करना?


11

मैं पर और बंद छोटे एम्बेडेड सिस्टम परियोजना पर काम कर रहा हूँ। इनमें से कुछ परियोजनाओं में एआरएम कॉर्टेक्स-एम 4 बेस प्रोसेसर का उपयोग किया गया था। प्रोजेक्ट फ़ोल्डर में एक स्टार्टअपS फ़ाइल है। उस फ़ाइल के अंदर मैंने निम्नलिखित दो कमांड लाइनें नोट कीं।

;******************************************************************************
;
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
;
;******************************************************************************
Stack   EQU     0x00000400

;******************************************************************************
;
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
;
;******************************************************************************
Heap    EQU     0x00000000

माइक्रोकंट्रोलर के लिए ढेर का आकार और स्टैक कैसे परिभाषित करता है? क्या सही मूल्य पर पहुंचने के लिए गाइडशीट में कोई विशेष जानकारी है? यदि हां, तो डेटशीट में क्या देखना चाहिए?


संदर्भ:

जवाबों:


12

ढेर और ढेर सॉफ्टवेयर अवधारणाएं हैं, न कि हार्डवेयर अवधारणाएं। हार्डवेयर जो प्रदान करता है वह मेमोरी है। मेमोरी के ज़ोन को परिभाषित करना, जिनमें से एक को "स्टैक" कहा जाता है और जिनमें से एक को "हीप" कहा जाता है, आपके कार्यक्रम का एक विकल्प है।

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

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

ढेर और स्टैक आकार चुनने के लिए, हार्डवेयर से एकमात्र प्रासंगिक जानकारी आपके पास कुल मेमोरी कितनी है। फिर आप अपनी पसंद के आधार पर अपनी पसंद का बना सकते हैं जो आप मेमोरी में स्टोर करना चाहते हैं (कोड, स्थैतिक डेटा और अन्य कार्यक्रमों के लिए अनुमति देते हैं)।

एक प्रोग्राम आम तौर पर इन स्थिरांक का उपयोग मेमोरी में कुछ डेटा को इनिशियलाइज़ करने के लिए करता है जो कि कोड के बाकी हिस्सों द्वारा उपयोग किया जाएगा, जैसे कि स्टैक के शीर्ष का पता, हो सकता है कि स्टैक ओवरफ्लो की जांच के लिए कहीं मान हो, ढेर आवंटन के लिए सीमाएं , आदि।

में कोड आप देख रहे हैं , Stack_Sizeलगातार कोड क्षेत्र में (एक के माध्यम से स्मृति का एक ब्लॉक आरक्षित किया जाता है SPACEविधानसभा एआरएम में निर्देश)। इस ब्लॉक के ऊपरी पते को लेबल दिया गया है __initial_sp, और यह वेक्टर टेबल में संग्रहीत है (प्रोसेसर एक सॉफ्टवेयर रीसेट के बाद एसपी को सेट करने के लिए इस प्रविष्टि का उपयोग करता है) और साथ ही अन्य स्रोत फ़ाइलों में उपयोग के लिए निर्यात किया जाता है। Heap_Sizeलगातार इसी तरह स्मृति का एक ब्लॉक आरक्षित करने के लिए इस्तेमाल और अपनी सीमाओं (किए जाने वाले लेबल है __heap_baseऔर __heap_limit) अन्य स्रोत फ़ाइलों में उपयोग के लिए निर्यात किया जाता है।

; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp


; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

…
__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler

…

                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit

क्या आप जानते हैं कि उन मूल्यों को कैसे 0x00200 और 0x000400 निर्धारित किया जाता है
महेंद्र

@MahendraGunawardena आपके कार्यक्रम की आवश्यकता के आधार पर, उन्हें निर्धारित करना आपके लिए है। नियाल का जवाब कुछ सुझाव देता है।
गिल्स एसओ- बुराई को रोकना '

7

स्टैक और हीप के आकार को आपके एप्लिकेशन द्वारा परिभाषित किया गया है, कहीं भी माइक्रोकंट्रोलर की डेटशीट में नहीं।

ढेर

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

एक एम्बेडेड सिस्टम विकसित करते समय, आप अनुमान लगाते हैं कि अधिकतम कॉल डेप्थ आपके पास है, उस पदानुक्रम में सभी स्थानीय चरों के आकारों को जोड़ें, फिर ऊपर बताए गए ओवरहेड के लिए कुछ पैडिंग जोड़ें, फिर कुछ और जोड़ें आपके कार्यक्रम के निष्पादन के दौरान होने वाली कोई भी व्यवधान।

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

ढेर

आप की जरूरत ढेर के आकार की गणना एक पेचीदा हो सकता है। ढेर इसलिए यदि आप का उपयोग गतिशील रूप से आवंटित चर के लिए प्रयोग किया जाता है, malloc()और free()एक सी भाषा कार्यक्रम में, या newऔर deleteC ++, निश्चित ही वे चर रहते हैं।

हालांकि, विशेष रूप से C ++ में, कुछ छिपे हुए गतिशील मेमोरी आवंटन हो सकते हैं। उदाहरण के लिए, यदि आपके पास सांख्यिकीय रूप से आवंटित वस्तुएं हैं, तो भाषा की आवश्यकता होती है कि कार्यक्रम से बाहर निकलने पर उनके विनाशकों को बुलाया जाए। मुझे कम से कम एक रनटाइम के बारे में पता है, जहां विध्वंसक के पते गतिशील रूप से आवंटित लिंक सूची में संग्रहीत हैं।

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


प्रतिक्रिया के लिए धन्यवाद, मैं 0x00400 और इसके बाद जैसे विशिष्ट संख्या का निर्धारण कैसे करना पसंद करता हूं
महेंद्र

5

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

स्टैक / हीप आवंटन कैसे काम करता है

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

ओपी के उदाहरण में, केवल 2 प्रतीकों को परिभाषित किया गया है, 1kiB पर ढेर का आकार और 0B पर ढेर का आकार। ये मान वास्तव में स्टैक और हीप स्पेस बनाने के लिए कहीं और उपयोग किए जाते हैं

@Gilles उदाहरण में, आकार को परिभाषित किया जाता है और असेंबली फ़ाइल में एक स्टैक स्पेस सेट करने के लिए उपयोग किया जाता है, जहाँ भी स्टैक स्पेस शुरू होता है और आकार को स्थायी करता है, जिसे प्रतीक Stack_Mem द्वारा पहचाना जाता है और अंत में एक लेबल __initial_sp सेट करता है। इसी तरह ढेर के लिए, जहां स्थान प्रतीक Heap_Mem (आकार में 0.5kiB) है, लेकिन शुरुआत और अंत में लेबल के साथ (__heap_base और __heap_limit)।

इन्हें लिंकर द्वारा संसाधित किया जाता है, जो स्टैक स्पेस और हीप स्पेस के भीतर कुछ भी आवंटित नहीं करेगा क्योंकि यह मेमोरी कब्जे में है (Stack_Mem और Heap_Mem प्रतीकों द्वारा), लेकिन यह उन यादों और सभी ग्लोबल्स को जगह दे सकता है जिनकी उन्हें आवश्यकता है। लेबल दिए गए पते पर बिना किसी लंबाई के प्रतीक हैं। लिंक समय पर वेक्टर तालिका के लिए सीधे __initial_sp का उपयोग किया जाता है, और आपके रनटाइम कोड द्वारा __heap_base और __heap_limit। प्रतीकों के वास्तविक पते लिंकर द्वारा असाइन किए जाते हैं, जहां यह उन्हें रखा गया है।

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

हालांकि, प्रत्येक टूलचैन अलग है, और कॉन्फ़िगरेशन फ़ाइल कैसे लिखनी है और आपके डायनामिक मेमोरी एलोकेटर का उपयोग करने वाले प्रतीकों को आपके विशेष वातावरण के प्रलेखन से आना होगा।

ढेर का आकार

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

हीप साइज़िंग

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

अंतिम नोट (RTOS)

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

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

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