En el artículo anterior , explicación detallada y análisis de ejemplo de archivos de enlace ICF en IAR , analicé la sintaxis de los archivos de enlace ICF en IAR a través de la relación de mapeo de memoria en el SDK de I.MX RT1170. Para el IDE utilizado en la programación de MCU, IAR y Keil se utilizan más, por lo que este artículo analizará los archivos dispersos de Keil .scf
( scatter file
).
Directorio de artículos
1 mapa de memoria
Al igual que en el artículo anterior, también utilizo el archivo de enlace en el SDK de I.MX RT1170 para el análisis y aprendo la gramática interna a través del archivo disperso real. Es la misma rutina que en el apartado anterior, además de la RAM que viene con el chip, están NOR Flash y SDRAM. Primero mire la tabla de mapeo de memoria de todo el proyecto:
tipo | nombre | dirección inicial | tamaño |
---|---|---|---|
Destello | Flash ni | 0x30000000 | 0x1000000 |
RAM | SDRAM | 0x80000000 | 0x3000000 |
RAM | NCACHE_REGION | 0x83000000 | 0x1000000 |
RAM | SRAM_DTC_cm7 | 0x20000000 | 0x40000 |
RAM | SRAM_ITC_cm7 | 0x0 | 0x40000 |
RAM | SRAM_OC1 | 0x20240000 | 0x80000 |
RAM | SRAM_OC2 | 0x202c0000 | 0x80000 |
RAM | SRAM_OC_ECC1 | 0x20340000 | 0x10000 |
RAM | SRAM_OC_ECC2 | 0x20350000 | 0x10000 |
Para nuestro proyecto, existen las siguientes memorias:
- Dos memorias de 256 KB estrechamente acopladas
DTCM
yITCM
- Dos RAM en chip con ECC:
OC1
yOC2
. 0x30000000
Se conecta un Flash NOR de 16 MB a la interfaz FlexSPI1 cuya dirección inicial es0x80000000
Se conecta una SDRAM de 64 MB a la interfaz FlexSPI2 con la dirección inicial del mapeo . Entre ellos, los primeros 48 MB se usan para el área almacenable en caché y los últimos 16 MB (NCACHE_REGION
) se usan para el área no almacenable en caché. Por lo general, el búfer que interactúa directamente con el hardware debe configurarse como no almacenable en caché.
2 análisis de sintaxis SCF
2.1 Archivo SCF del proyecto
Para el mapeo de memoria anterior, los archivos proporcionados en el SDK oficial SCF
son los siguientes:
#if (defined(__ram_vector_table__))
#define __ram_vector_table_size__ 0x00000400
#else
#define __ram_vector_table_size__ 0x00000000
#endif
#define m_flash_config_start 0x30000400
#define m_flash_config_size 0x00000C00
#define m_ivt_start 0x30001000
#define m_ivt_size 0x00000020
#define m_boot_data_start 0x30001020
#define m_boot_data_size 0x00000010
#define m_dcd_data_start 0x30001030
#define m_dcd_data_size 0x000006E8
#define m_xmcd_data_start 0x30001040
#define m_xmcd_data_size 0x00000204
#define m_interrupts_start 0x30002000
#define m_interrupts_size 0x00000400
#define m_text_start 0x30002400
#if (defined(__use_flash64MB__))
#define m_text_size 0x03FFDC00
#else
#define m_text_size 0x00FFDC00
#endif
#define m_qacode_start 0x00000000
#define m_qacode_size 0x00040000
#define m_interrupts_ram_start 0x80000000
#define m_interrupts_ram_size __ram_vector_table_size__
#define m_data_start (m_interrupts_ram_start + m_interrupts_ram_size)
#define m_data_size (0x03000000 - m_interrupts_ram_size)
#define m_data2_start 0x20000000
#define m_data2_size 0x00040000
#define m_data3_start 0x202C0000
#define m_data3_size 0x00080000
#define m_ncache_start 0x83000000
#define m_ncache_size 0x01000000
/* Sizes */
#if (defined(__stack_size__))
#define Stack_Size __stack_size__
#else
#define Stack_Size 0x0400
#endif
#if (defined(__heap_size__))
#define Heap_Size __heap_size__
#else
#define Heap_Size 0x0400
#endif
#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1)
LR_m_text m_flash_config_start m_text_start+m_text_size-m_flash_config_start { ; load region size_region
RW_m_config_text m_flash_config_start FIXED m_flash_config_size { ; load address = execution address
* (.boot_hdr.conf, +FIRST)
}
RW_m_ivt_text m_ivt_start FIXED m_ivt_size { ; load address = execution address
* (.boot_hdr.ivt, +FIRST)
}
RW_m_boot_data_text m_boot_data_start FIXED m_boot_data_size { ; load address = execution address
* (.boot_hdr.boot_data, +FIRST)
}
#if defined(XIP_BOOT_HEADER_DCD_ENABLE) && (XIP_BOOT_HEADER_DCD_ENABLE == 1)
RW_m_dcd_data_text m_dcd_data_start FIXED m_dcd_data_size { ; load address = execution address
* (.boot_hdr.dcd_data, +FIRST)
}
#elif defined(XIP_BOOT_HEADER_XMCD_ENABLE) && (XIP_BOOT_HEADER_XMCD_ENABLE == 1)
RW_m_xmcd_data_text m_xmcd_data_start FIXED m_xmcd_data_size { ; load address = execution address
* (.boot_hdr.xmcd_data, +FIRST)
}
#endif
#else
LR_m_text m_interrupts_start m_text_start+m_text_size-m_interrupts_start { ; load region size_region
#endif
VECTOR_ROM m_interrupts_start FIXED m_interrupts_size { ; load address = execution address
* (.isr_vector,+FIRST)
}
ER_m_text m_text_start FIXED m_text_size { ; load address = execution address
* (InRoot$$Sections)
.ANY (+RO)
}
#if (defined(__ram_vector_table__))
VECTOR_RAM m_interrupts_ram_start EMPTY m_interrupts_ram_size {
}
#else
VECTOR_RAM m_interrupts_start EMPTY 0 {
}
#endif
RW_m_data2 m_data2_start m_data2_size {
* (RamFunction)
* (DataQuickAccess)
}
#if (defined(__heap_noncacheable__))
RW_m_data m_data_start m_data_size-Stack_Size { ; RW data
#else
RW_m_data m_data_start m_data_size-Stack_Size-Heap_Size { ; RW data
#endif
.ANY (+RW +ZI)
*(*m_usb_dma_init_data)
*(*m_usb_dma_noninit_data)
}
#if (!defined(__heap_noncacheable__))
ARM_LIB_HEAP +0 EMPTY Heap_Size { ; Heap region growing up
}
#endif
ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size { ; Stack region growing down
}
RW_m_ram_text m_qacode_start m_qacode_size { ;
* (CodeQuickAccess)
}
#if (defined(__heap_noncacheable__))
RW_m_ncache m_ncache_start m_ncache_size - Heap_Size { ; ncache data
#else
RW_m_ncache m_ncache_start m_ncache_size { ; ncache data
#endif
* (NonCacheable.init)
* (*NonCacheable)
}
#if (defined(__heap_noncacheable__))
ARM_LIB_HEAP +0 EMPTY Heap_Size { ; Heap region growing up
}
RW_m_ncache_unused +0 EMPTY m_ncache_size-ImageLength(RW_m_ncache)-Heap_Size { ; Empty region added for MPU configuration
#else
RW_m_ncache_unused +0 EMPTY m_ncache_size-ImageLength(RW_m_ncache) { ; Empty region added for MPU configuration
#endif
}
}
2.2 definir
Primero analicemos el primer párrafo de archivos dispersos. La sintaxis define
y #if defined
las declaraciones de los archivos dispersos de KEIL son consistentes con el lenguaje C, por lo que el siguiente párrafo es fácil de entender:
#if (defined(__ram_vector_table__))
#define __ram_vector_table_size__ 0x00000400
#else
#define __ram_vector_table_size__ 0x00000000
#endif
#define m_flash_config_start 0x30000400
#define m_flash_config_size 0x00000C00
#define m_ivt_start 0x30001000
#define m_ivt_size 0x00000020
#define m_boot_data_start 0x30001020
#define m_boot_data_size 0x00000010
#define m_dcd_data_start 0x30001030
#define m_dcd_data_size 0x000006E8
#define m_xmcd_data_start 0x30001040
#define m_xmcd_data_size 0x00000204
#define m_interrupts_start 0x30002000
#define m_interrupts_size 0x00000400
#define m_text_start 0x30002400
#if (defined(__use_flash64MB__))
#define m_text_size 0x03FFDC00
#else
#define m_text_size 0x00FFDC00
#endif
#define m_qacode_start 0x00000000
#define m_qacode_size 0x00040000
#define m_interrupts_ram_start 0x80000000
#define m_interrupts_ram_size __ram_vector_table_size__
#define m_data_start (m_interrupts_ram_start + m_interrupts_ram_size)
#define m_data_size (0x03000000 - m_interrupts_ram_size)
#define m_data2_start 0x20000000
#define m_data2_size 0x00040000
#define m_data3_start 0x202C0000
#define m_data3_size 0x00080000
#define m_ncache_start 0x83000000
#define m_ncache_size 0x01000000
/* Sizes */
#if (defined(__stack_size__))
#define Stack_Size __stack_size__
#else
#define Stack_Size 0x0400
#endif
#if (defined(__heap_size__))
#define Heap_Size __heap_size__
#else
#define Heap_Size 0x0400
#endif
Permítanme explicarles primero, cuando el microcontrolador de la serie I.MX esté encendido, ingresará al cargador de arranque L1, que se usa para iniciar cómo iniciar el programa, por ejemplo, si está cifrado, la clave de cifrado, si es XIP o no XIP (debe copiarse en la RAM) y si es necesario inicializarlo. En el caso del inicio de NOR Flash, los primeros 0x2000 bytes de la imagen del programa se utilizan para proporcionar información de inicio al cargador de arranque L1. No es necesario prestar demasiada atención al significado de estos campos. Si desea comprender en Para obtener más detalles, puede consultar mi artículo I.MX Explicación detallada del inicio de RT1170: Configuración de inicio y composición del encabezado de imagen de inicio .
(1) __ram_vector_table__
no está definido en ningún otro lugar, por lo que __ram_vector_table_size__
es 0. Esto también es fácil de entender, porque aquí hay NOR Flash, la tabla de vectores no se coloca en la RAM, sino que se coloca al frente de NOR Flash.
(2) m_flash_config_start
y m_flash_config_size
: se utiliza para proporcionar información de configuración de NOR Flash al L1 BootLoader, porque después del encendido, L1 BootLoader utiliza la configuración más lenta y segura para inicializar NOR Flash. Si el usuario desea configurar algunos parámetros por sí mismo, como hacer el reloj más rápido. Puede completar la información de configuración en este campo, la dirección inicial es 0x30000400
(la dirección base de NOR Flash es 0x30000000
), la longitud es 0xC00
(3) m_ivt_start
y m_ivt_size
: Image Vector Table
campo IVT (), utilizado para guardar parámetros como la entrada del programa. dirección
(4) m_boot_data_start
y m_boot_data_size
: usado Guarde la dirección inicial absoluta y el tamaño de la imagen
(5) m_dcd_data_start
y m_dcd_data_size
: DCD
campos, que generalmente se usan para inicializar SDRAM. Especialmente si desea que el programa se ejecute en SDRAM, debe configurar este campo
( 6) m_xmcd_data_start
ym_xmcd_data_size
: Puede ver que la dirección inicial y el tamaño aquí DCD
se superponen con los campos anteriores. De hecho, las funciones de los dos son similares, excepto que DCD
la configuración es una instrucción de configuración de registro, que es más complicada y XMCD simplifica estas operaciones de configuración. Estos dos campos son opcionales.
(7) m_interrupts_start
y m_interrupts_size
: Como se mencionó anteriormente, el tamaño de la información del encabezado del BootLoader L1 es 0x2000
, por lo que 0x2000
es el comienzo del programa desde el principio, y la tabla de vectores se coloca al frente y la longitud es 0x400
.
(8) m_text_start
y m_text_size
: El segmento de código está inmediatamente después de la tabla de vectores, la dirección inicial es 0x30002400
, aquí __use_flash64MB__
es falso, asumimos que se usan 16 MB () NOR Flash 0x1000000
y el tamaño restante es 0x1000000-0x2400=0x00FFDC00
.
(9) m_qacode_start
Suma : es decir , (10) m_qacode_size
suma dentro del chip en el mapa de memoria anterior : si la tabla de vectores no se coloca en NOR Flash, se coloca al comienzo de SDRAM. Dado que se coloca en NOR Flash, estos No se utilizan dos campos (11) y : segmento de datos de datos, donde el segmento de datos se coloca en SDRAM. Aquí el tamaño de SDRAM es 64M y este campo ocupa los primeros 48M. (12) , y : el mismo segmento de datos, respectivamente y , es decir, la RAM en el chip se puede utilizar como segmento de datos para colocar variablesSRAM_ITC_cm7
m_interrupts_ram_start
m_interrupts_ram_size
m_data_start
m_data_size
m_data2_start
m_data2_size
m_data3_start
m_data3_size
SRAM_ITC_cm7
SRAM_OC2
SRAM_OC1
Si no se utiliza, podemos declararlo nosotros mismos. Debido a que L1 BootLoader usa esta SRAM cuando se ejecuta, debe considerar el tiempo al usar esta SRAM.
(13) m_ncache_start
Suma m_ncache_size
: Los últimos 16 M de SDRAM se utilizan para áreas que no se pueden almacenar en caché, como el búfer de dibujo de la GUI, el búfer de la cámara y los datos DMA. Este tipo de memoria que interactúa directamente con el hardware debe definirse en un área que no se puede almacenar en caché. Esto está relacionado con la configuración de MPU. Puede consultar los artículos de mi serie MPU, explicación detallada y ejemplos de la unidad de protección de memoria MPU y explicación detallada de L1 Cache I-Cache y D-cache .
(14) Stack_Size
y Heap_Size
: son los tamaños de pila y montón respectivamente. Dado que FreeRTOS se utiliza en el programa, solo asegúrese de que los tamaños de pila y montón aquí puedan inicializar correctamente FreeRTOS. No debe haber ninguna asignación de memoria durante el proceso de inicialización de FreeRTOS, por lo que se puede establecer en 0 Heap Size
. .
2.3 Zona de carga y zona de ejecución
A continuación, comenzaremos a hablar sobre la sintaxis de algunos archivos dispersos, consulte la documentación: <DUI0377G_02_mdk_armlink_user_guide.pdf>
(se puede encontrar en el directorio de instalación de KEIL).
En comparación con IAR, la sintaxis de los archivos dispersos de KEIL es mucho más simple, similar a la del archivo ld de Linux. Los archivos dispersos se componen de una o más áreas de carga (), como se muestra en la siguiente figura: La sintaxis Load Region
del
área de carga es como sigue:
load_region_name (base_address | ("+" offset)) [attribute_list] [max_size]
"{"
execution_region_description+
"}"
load_region_name
(nombre): una etiqueta única utilizada para identificar diferentes áreas de carga por parte del vinculador, cada área de carga debe tener un nombre únicobase_address
(Dirección base): la dirección de memoria inicial donde se colocan en la memoria el código y los datos en el área de carga.attribute_list
(propiedad): define las características y el comportamiento del área de carga, incluidos los atributos de solo lectura, lectura-escritura, solo ejecución u otros atributos de protección de memoria.max_size
(tamaño máximo): opcional, se utiliza para limitar el tamaño del área de carga para evitar el desbordamiento de la memoriaexecution_region_description
(Región de ejecución): la región de carga puede contener una o más regiones de ejecución. La región de ejecución representa un bloque contiguo de código y datos cargados en la memoria como una sola unidad.
Sería demasiado tiempo resumir toda la gramática en el artículo, por lo que debemos continuar analizando los documentos dispersos, si aparece alguna gramática o palabra clave, encontraremos su significado. Dado que hay demasiadas definiciones de macros en los siguientes archivos dispersos, lo que afecta la lectura, aquí se supone que , XIP_BOOT_HEADER_ENABLE=1
y ( significan que el montón se coloca en el área que no se puede almacenar en caché para garantizar que la XIP_BOOT_HEADER_DCD_ENABLE=1
memoria del montón no se vea afectada por el cache).XIP_BOOT_HEADER_XMCD_ENABLE=0
__heap_noncacheable__
Los archivos dispersos restantes en realidad definen un área de carga LR_m_text
. Su dirección inicial es m_flash_config_start
( 0x30000400
) y su tamaño máximo es m_text_start+m_text_size-m_flash_config_start
( 16M-0x400=0xFFFC00
), es decir, 0x30000400
el enlace comienza desde allí y el tamaño es 0xFFFC00
. Este tamaño solo limita el tamaño del área de carga (el siguiente atributos para FIXED
el área de ejecución). 0~0x400
Estos campos, relevantes para el inicio cifrado de los microcontroladores de la serie NXP RT, no pueden ser completados por el compilador, por lo que no se consideran aquí.
LR_m_text m_flash_config_start m_text_start+m_text_size-m_flash_config_start { ; load region size_region
......
}
- En archivos dispersos,
;
seguidos de comentarios.
LR_m_text
Hay muchas áreas de ejecución debajo del área de carga , analicémoslas una por una:
1.RW_m_config_text
: dirección inicial 0x30000400
, tamaño0xC00
RW_m_config_text m_flash_config_start FIXED m_flash_config_size { ; load address = execution address
* (.boot_hdr.conf, +FIRST)
}
FIXED
: Atributos del área de ejecución, que indican que la dirección de ejecución y la dirección de carga del área de ejecución deben mantenerse iguales tanto como sea posible. Esto significa que el código y los datos asignados a esta región de ejecución intentarán colocarse en la dirección de ejecución especificada cuando se carguen en la memoria. Si no se puede satisfacer debido a conflictos de memoria o espacio insuficiente, el vinculador informará un error.+FIRST
: Indicasection
colocar la función al inicio del área de ejecución
Entonces aquí colocamos boot_hdr.conf
el segmento desde el comienzo de 0x30000400. Debido a que la posición de ubicación debe ser fija para que L1 BootLoader la reconozca correctamente, el área de ejecución debe usar FIXED
atributos.
2.RW_m_ivt_text
: dirección inicial 0x30001000
, tamaño0x00000020
RW_m_ivt_text m_ivt_start FIXED m_ivt_size { ; load address = execution address
* (.boot_hdr.ivt, +FIRST)
}
Igual que arriba, coloque el encabezado de arranque de L1 BootLoader.
3.RW_m_boot_data_text
: dirección inicial 0x30001020
, tamaño0x00000010
RW_m_boot_data_text m_boot_data_start FIXED m_boot_data_size { ; load address = execution address
* (.boot_hdr.boot_data, +FIRST)
}
Igual que arriba, coloque el encabezado de arranque de L1 BootLoader.
4.RW_m_dcd_data_text
: dirección inicial 0x30001030
, tamaño0x000006E8
RW_m_dcd_data_text m_dcd_data_start FIXED m_dcd_data_size { ; load address = execution address
* (.boot_hdr.dcd_data, +FIRST)
}
Igual que arriba, coloque el encabezado de arranque de L1 BootLoader.
5.VECTOR_ROM
: dirección inicial 0x30002000
, tamaño0x00000400
VECTOR_ROM m_interrupts_start FIXED m_interrupts_size { ; load address = execution address
* (.isr_vector,+FIRST)
}
Coloque la tabla de vectores de interrupción. startup_MIMXRT1176_cm7.S
Esta sección está definida en el archivo de inicio :, .section .isr_vector, "a"
lo que a
significa que la sección está marcada como asignable ( allocatable
), lo que significa que se puede asignar a una determinada ubicación en la memoria en el momento del enlace.
6.ER_m_text
: dirección inicial 0x30002400
, tamaño0x00FFDC00
ER_m_text m_text_start FIXED m_text_size { ; load address = execution address
* (InRoot$$Sections)
.ANY (+RO)
}
InRoot$$Sections
Es una marca especial que se utiliza en archivos dispersos para colocar segmentos de datos comprimidos en el área de ejecución para garantizar que estos segmentos de datos puedan descomprimirse automáticamente y proporcionarse al programa en tiempo de ejecución. Esta característica está diseñada por ARM para reducir el uso del espacio de almacenamiento. Después del encendido, la biblioteca ARM se descomprimirá de acuerdo con esta sección.- Texto de referencia: Ejemplo de colocación de código en una región raíz
.ANY
: puede entenderse como que*
indica todos los segmentos, pero.ANY
se puede usar en múltiples áreas de ejecución, pero*
generalmente solo se usa en un área de ejecución, por lo.ANY
que es más flexible. Consulte el Capítulo 7.4 del manual para obtener más detalles.<Placement of unassigned sections with the .ANY module selector>
(+RO)
: segmento de datos de solo lectura
Esto significa colocar todos los segmentos de datos de solo lectura en esta área de ejecución.
7VECTOR_RAM
.: La tabla de vectores de interrupción se coloca aquí en NOR Flash, esta área de ejecución no se utiliza.
VECTOR_RAM m_interrupts_start EMPTY 0 {
}
EMPTY
Indica que se reserva un área vacía, pero el tamaño de esta área se establece en 0, por lo que esta área de ejecución no tiene ningún efecto.
8.RW_m_data2
: dirección inicial 0x20000000
, tamaño0x00040000
RW_m_data2 m_data2_start m_data2_size {
* (RamFunction)
* (DataQuickAccess)
}
Aquí se definen dos secciones: RamFunction
y DataQuickAccess
, que se pueden __attribute__((section("")))
utilizar para SRAM_DTCM
, lo que puede acelerar la ejecución de funciones y el acceso a datos.
9.RW_m_data
: dirección inicial 0x80000000
, tamaño0x03000000-0x400
RW_m_data m_data_start m_data_size-Stack_Size { ; RW data
.ANY (+RW +ZI)
*(*m_usb_dma_init_data)
*(*m_usb_dma_noninit_data)
}
Esta área de ejecución son los primeros 48 M de SDRAM. Aquí se colocan todos los segmentos de datos de lectura y escritura y los segmentos bss, y se declaran dos segmentos USB al mismo tiempo para la implementación de funciones relacionadas con USB en el SDK. De hecho, el segmento USB non-cacheable
definitivamente puede ejecutarse si se coloca en la zona, pero también significa que el caché no se usa y la velocidad se reducirá mucho. Por lo tanto, puede declarar variables relacionadas con USB en cacheable
el área y luego almacenar en caché y actualizar manualmente las funciones relacionadas en los lugares necesarios del código, como SCB_CleanInvalidateDCache
y SCB_CleanDCache
.
10.ARM_LIB_STACK
: dirección inicial 0x83000000
, tamaño0x400
ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size { ; Stack region growing down
}
ARM_LIB_STACK
: Nombre fijo del área de ejecución de la pila.
Stack_Size
Hay uno delante de aquí -
, lo que indica que la pila crece hacia abajo.
11.RW_m_ram_text
: dirección inicial 0x00000000
, tamaño0x00040000
RW_m_ram_text m_qacode_start m_qacode_size { ;
* (CodeQuickAccess)
}
Similar al punto 7 anterior, declare un CodeQuickAccess
segmento para vincular funciones a la SRAM_ITCM
SRAM interna porque la velocidad de acceso de la SRAM interna es mucho más rápida que la de NOR Flash o SDRAM.
12.RW_m_ncache
: dirección inicial 0x83000000
, tamaño0x01000000-0x400
RW_m_ncache m_ncache_start m_ncache_size - Heap_Size { ; ncache data
* (NonCacheable.init)
* (*NonCacheable)
}
Defina non-cacheable
los dos segmentos del área NonCacheable.init
y NonCacheable
reserve el espacio del montón al mismo tiempo, porque aquí asumimos que el espacio del montón también lo es non-cacheable
.
13.ARM_LIB_HEAP
: Tamaño0x400
ARM_LIB_HEAP +0 EMPTY Heap_Size { ; Heap region growing up
}
+0
: Representa la dirección final del área de ejecución anterior,RW_m_ncache
esta dirección se determina en función del número y tamaño de las variables colocadas por el usuario en el área de ejecución.ARM_LIB_HEAP
: Nombre fijo del área de ejecución del montón.
Define la memoria del espacio del montón.
14 、RW_m_ncache_unused
RW_m_ncache_unused +0 EMPTY m_ncache_size-ImageLength(RW_m_ncache)-Heap_Size { ; Empty region added for MPU configuration
}
También se coloca en la dirección final del área de ejecución anterior para configurar la MPU. El tamaño es el área m_ncache_size-ImageLength(RW_m_ncache)-Heap_Size
restante non-cacheable
fuera del montón excepto las variables.
ImageLength
Puedes tomar el tamaño de un área de ejecución.