Bir ARM Cortex-M4 mikrokontrolcüsü için yığın ve yığın büyüklüğünü tanımlama?


11

Açık ve kapalı küçük gömülü sistemler projesi üzerinde çalışıyorum. Bu projelerden bazıları bir ARM Cortex-M4 temel işlemci kullandı. Proje klasöründe bir startup.s dosya. Bu dosyanın içinde aşağıdaki iki komut satırını not ettim.

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

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

Kişi boyutu nasıl tanımlanır? yığın ve yığın Bir mikrodenetleyici için? Veri sayfasında, doğru değere ulaşma konusunda rehberlik edecek belirli bir bilgi var mı? Öyleyse, veri sayfasında ne aranmalıdır?


Referanslar:

Yanıtlar:


11

Yığın ve öbek, donanım kavramları değil yazılım kavramlarıdır. Donanımın sağladığı hafıza hafızadır. Biri “yığın” ve biri “yığın” olarak adlandırılan hafıza bölgelerinin tanımlanması, programınızın bir seçimidir.

Donanım yığınlarla yardımcı olur. Çoğu mimaride yığın işaretçisi adı verilen özel bir kayıt defteri vardır. Kullanım amacı, program bir işlev çağrısı yaptığında, işlev parametreleri ve dönüş adresinin yığına itilmesi ve işlev sona erdiğinde ve çağrıcısına geri döndüğünde atılmalarıdır. Yığın üzerine itmek, yığın işaretçisinin verdiği adrese yazmak ve yığın işaretçisini buna göre azaltmak (veya yığının hangi yönde büyüdüğüne bağlı olarak artırmak) anlamına gelir. Patlatma, yığın işaretçisini artırmak (veya azaltmak) anlamına gelir; dönüş adresi yığın işaretçisi tarafından verilen adresten okunur.

Bazı mimarilerde (ARM olmasa da), bir atlama ile yığın işaretçisi tarafından verilen adrese yazıyı birleştiren bir alt yordam çağrısı talimatı ve yığın işaretçisi tarafından verilen adresten okumayı ve bu adrese atlamayı birleştiren bir alt yordam geri dönüş komutu bulunur. ARM'de, adres kaydetme ve geri yükleme işlemi LR kaydında yapılır, arama ve iade talimatları yığın işaretçisini kullanmaz. Bununla birlikte, fonksiyon argümanlarını itmek ve açmak için yığın göstericisi tarafından verilen adrese birden fazla yazmaç yazmayı veya okumayı kolaylaştıran talimatlar vardır.

Yığın ve yığın boyutunu seçmek için, donanıma ait tek ilgili bilgi toplam hafıza miktarınızdır. Ardından, bellekte ne depolamak istediğinize bağlı olarak seçiminizi yaparsınız (kod, statik veriler ve diğer programlar için izin verilir).

Bir program tipik olarak, bu sabitleri, yığının en üstünün adresi, örneğin yığın taşmalarını kontrol etmek için bir yere, yığın yığınlandırıcı için sınırlar gibi, kodun geri kalanı tarafından kullanılacak olan bellekteki bazı verileri başlatmak için kullanır. , vb.

İçinde Baktığınız kod , Stack_Size Sabit kod alanında bir bellek bloğu ayırmak için kullanılır ( SPACE ARM montajında ​​direktif). Bu bloğun üst adresi etiketi verilmiştir. __initial_spve vektör tablosunda depolanır (işlemci, bir yazılımı sıfırladıktan sonra SP'yi ayarlamak için bu girişi kullanır) ve diğer kaynak dosyalarda kullanılmak üzere dışa aktarılır. Heap_Size sabiti benzer şekilde sınırlarına bir hafıza ve etiket bloğu ayırmak için kullanılır ( __heap_base ve __heap_limit ) diğer kaynak dosyalarda kullanılmak üzere dışa aktarılır.

; 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 ve 0x000400 değerlerinin nasıl belirlendiğini biliyor musunuz?
Mahendra Gunawardena

@MahendraGunawardena Programınızın ihtiyaç duyduğu temelde onları belirlemek size kalmış. Niall'in cevabı birkaç ipucu veriyor.
Gilles

7

Yığın ve yığının boyutları, mikro denetleyicinin veri sayfasında hiçbir yerde değil, uygulamanız tarafından tanımlanır.

Yığını

Yığın, fonksiyonların içindeki yerel değişkenlerin değerlerini, yerel değişkenler için kullanılan önceki CPU yazmaçlarının değerlerini (böylece işlevden çıktıklarında geri yüklenebilir), bu işlevlerden çıkarken geri dönecekleri program adreslerini depolamak için kullanılır. yığının kendisinin yönetimi için bazı ek yükü.

Gömülü bir sistem geliştirirken, beklediğiniz maksimum arama derinliğini tahmin eder, oradaki hiyerarşideki fonksiyonlardaki tüm yerel değişkenlerin boyutlarını toplar, sonra yukarıda belirtilen ek yükler için izin vermek için biraz dolgu ekleyin ve Programınızın yürütülmesi sırasında oluşabilecek her türlü kesinti.

Alternatif bir tahmin yöntemi (RAM'in kısıtlanmadığı yerlerde), ihtiyaç duyduğunuzdan çok daha fazla yığın alanı ayırmak, yığını bir sentinel değeriyle doldurmak ve daha sonra yürütme sırasında gerçekte ne kadar kullandığınızı izlemek. Bunu sizin için otomatik olarak yapacak olan C dili çalışma zamanlarının hata ayıklama sürümlerini gördüm. Ardından, geliştirmeyi tamamladığınızda, isterseniz yığın boyutunu azaltabilirsiniz.

Yığın

İhtiyacınız olan yığının boyutunu hesaplamak daha zor olabilir. Öbek dinamik olarak ayrılan değişkenler için kullanılır. malloc() ve free() bir C dili programında veya new ve delete C ++ 'da, bu değişkenlerin yaşadığı yer.

Bununla birlikte, özellikle C ++ 'da, devam eden bazı gizli dinamik bellek ayırmaları olabilir. Örneğin, statik olarak ayrılmış nesneleriniz varsa, dil, program çıkarken onların yıkıcılarının çağrılmasını gerektirir. Yıkıcıların adreslerinin dinamik olarak ayrılmış bir bağlı listede depolandığı en az bir çalışma zamanının farkındayım.

İhtiyacınız olan yığının boyutunu tahmin etmek için, çağrı ağacınızdaki her yoldaki tüm dinamik bellek dağılımına bakın, maksimum değeri hesaplayın ve biraz dolgu yapın. Dil çalışma zamanı, toplam yığın kullanımını, parçalanmayı vb. İzlemek için kullanabileceğiniz tanılamalar sağlayabilir.


Yanıtınız için teşekkür ederiz, 0x00400 gibi belirli sayıların nasıl belirleneceğini severim.
Mahendra Gunawardena

5

Diğer cevaplara ek olarak, yığın ve yığın alanı arasında RAM oluşturulurken, statik olmayan sabit verilerin (örneğin, globals, fonksiyon statiği ve program genelinde dosya statik) alanını da göz önünde bulundurmanız gerektiğini eklemek isterim. C perspektifinden globaller ve muhtemelen C ++ için diğerleri.

Yığın / yığın tahsisi nasıl çalışır?

Başlangıç ​​derleme dosyasının, bölgeyi tanımlamanın bir yolu olduğuna dikkat etmek gerekir; takım zinciri (hem yapı ortamınız hem de çalışma zamanı ortamınız) çoğunlukla, yığın uzayının başlangıcını (ilk yığın işaretçisini Vector Tablosunda depolamak için kullanılır) ve yığın uzayının başlangıcını ve sonunu (dinamik tarafından kullanılan) tanımlayan sembolleri önemser. hafıza dağıtıcısı, genellikle libc tarafından sağlanan)

OP örneğinde, sadece 2 sembol tanımlanmıştır, 1kiB'de yığın büyüklüğü ve 0B'de yığın büyüklüğü. Bu değerler aslında yığın ve yığın alanlarını üretmek için başka yerlerde kullanılır.

@Gilles örneğinde, boyutlar tanımlanmıştır ve kullanılmış montaj dosyasında, her yerden başlayarak ve boyutuna devam eden bir yığın alanı ayarlamak için, Stack_Mem sembolü ile tanımlanır ve sonunda __initial_sp etiketini ayarlar. Aynı şekilde, boşluğun Heap_Mem (0,5kiB boyutunda) sembolü olduğu, ancak başında ve sonunda etiketlerin bulunduğu yığın için (__heap_base ve __heap_limit).

Bunlar, bağlayıcı tarafından işlenir; bu, yığın belleği ve yığın alanı içinde hiçbir şey tahsis etmeyecektir, çünkü bu bellek kullanılır (Stack_Mem ve Heap_Mem sembolleri tarafından), ancak bu hatıraları ve tüm globals'ları gereken yere yerleştirebilir. Etiketler, verilen adreslerde uzunluğu olmayan semboller olur. __İnitial_sp, bağlantı zamanında doğrudan vektör tablosu için ve çalışma zamanı kodunuz tarafından __heap_base ve __heap_limit'te kullanılır. Sembollerin gerçek adresleri linker tarafından yerleştirildiği yere göre atanır.

Yukarıda belirtildiği gibi, bu sembollerin aslında bir startup.s dosyasından gelmeleri gerekmiyor. Bağlayıcı yapılandırmanızdan (Keil'deki Dağılım Yük dosyası, GNU'daki linkercript) ve yerleştirme üzerinde daha hassas taneli kontrole sahip olabilirsiniz. Örneğin, yığını RAM'in başında veya sonunda olmaya zorlayabilir ya da kürelerinizi öbeklerden uzakta veya istediğiniz şekilde uzak tutabilirsiniz. HEAP veya STACK'in, globall'lar yerleştirildikten sonra geride kalan RAM'i işgal ettiğini bile belirleyebilirsiniz. DİKKAT, diğer belleğinizin azaltacağı daha fazla statik değişken eklemek konusunda dikkatli olmanız gerekir.

Bununla birlikte, her bir araç zinciri farklıdır ve yapılandırma dosyasını nasıl yazacağınızı ve dinamik bellek ayırıcınızın hangi sembolleri kullanacağını kendi ortamınızın dokümantasyonundan elde etmeniz gerekecektir.

Yığın Boyutlandırma

Yığın boyutunun nasıl belirleneceği ile ilgili olarak, birçok takım zinciri, programınızın fonksiyon çağrısı ağaçlarını analiz ederek size maksimum yığın derinliği verebilir, özyineleme veya fonksiyon işaretçileri kullanmazsanız. Bunları kullanırsanız, yığın büyüklüğünü tahmin edip kardinal değerlerle önceden doldurma (belki ana sistemden önce giriş fonksiyonu aracılığıyla) ve ardından programınızın bir süre boyunca çalışıp çalışmadığını kontrol edin (maksimum değerin olduğu yer) son). Programınızı sınırlarını tam olarak uyguladıysanız, yığını küçültüp küçültemeyeceğinizi veya programınız çökerse veya kardinal değer kalmadıysa yığını arttırmanız ve yeniden denemeniz gerekeceğini oldukça doğru bir şekilde bilirsiniz.

Yığın Boyutlandırma

Yığın boyutunu belirlemek biraz daha uygulamaya bağlıdır. Başlatma sırasında yalnızca dinamik ayırma yaparsanız, başlangıç ​​kodunuzda gereken boşluğu ekleyebilirsiniz (artı bellek yönetimi için bazı ek yükler). Hafıza yöneticinizin kaynağına erişiminiz varsa, ek yükün tam olarak ne olduğunu bilirsiniz ve muhtemelen kullanım bilgisini vermek için hafızayı dolaşmak için kod bile yazabilirsiniz. Dinamik çalışma zamanı belleği gerektiren uygulamalar için (örneğin, gelen ethernet çerçeveleri için arabellek ayırma) önerebileceğim en iyi şey yığın boyutunuzu dikkatlice bilemek ve Yığın'a yığın ve statikten sonra kalan her şeyi vermektir.

Son not (RTOS)

OP'nin sorusu yalın metal olarak etiketlendi, ancak RTOS'lar için bir not eklemek istiyorum. Genelde (her zaman?) Her görev / süreç / iş parçacığı (sadece burada basitlik için burada görev yazacağım), görev oluşturulduğunda bir yığın boyutu atanır, görev yığınlarına ek olarak, küçük bir işletim sistemi olabilir. yığın (kesintiler ve benzeri için kullanılır)

Görev muhasebe yapıları ve yığınlar bir yerden tahsis edilmek zorundadır ve bu genellikle uygulamanızın genel yığın alanından olacaktır. Bu durumlarda, başlangıç ​​yığın boyutunuz çoğu zaman önemli olmaz, çünkü işletim sistemi yalnızca başlatma sırasında kullanır. Örneğin, bağlantı sırasında TÜM kalan boşluğun HEAP'a tahsis edildiğini ve ilk yığın göstergesinin yığının sonuna gelmek üzere yığının sonuna yerleştirileceğini, işletim sisteminin yığının başından başlayarak başlayacağını bilerek gördüm ve OS yığını, initial_sp yığınını terk etmeden hemen önce tahsis eder. Ardından, tüm alan görev yığınlarını ve diğer dinamik olarak ayrılmış belleği tahsis etmek için kullanılır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.