Temario Las Variables en Embebidos

Transcripción

Temario Las Variables en Embebidos
Sistemas Embebido 2010 - C para Embebidos ARM
07/09/2010
Construcciones y Formas del
Lenguaje C para Embebidos
Sistemas Embebidos 2010
C para Embebidos ARM
„
Las Variables en Embebidos
„
„
Ing. Juan Manuel Cruz ([email protected])
Profesor Invitado
„
Seminario de Electrónica: Sistemas Embebidos (Cód: 66.48)
„
Ingeniería en Electrónica – FI – UBA
„
Buenos Aires, 7 de Septiembre de 2010
07/09/2010
Temario
„
„
„
Construcciones y Formas del Lenguaje C
para Embebidos
Consideraciones para codificar en C sobre
procesadores ARM
M
Mapa
de
d memoria
i
Consideraciones adicionales y conclusiones
07/09/2010
Ing. Juan Manuel Cruz
Ing. Juan Manuel Cruz
3
Las Variables en Embebidos
„
„
Residen en un ambiente muy diferente al de una
PC
Pueden residir en memoria de lectura solamente
y/o en posiciones de memoria fija
Pueden cambiar de valor debido a eventos del
hardware (asincrónicos al flujo del programa)
Escribir en ellas pueden ser ilegal y causar
excepciones de sistema
Pueden cambiar de valor al ser leídas
Ing. Juan Manuel Cruz
„
2
El lenguaje C posee elementos para señalar éstas
características al compilador, permitiéndole hacer
hipótesis correctas sobre el acceso a las mismas y la
correcta generación de secuencias de código
Se recurre a palabras claves especiales que califican
el tipo de la variable (se definen claramente las
clases del almacenamiento en el contexto del
embebido más que en el contexto general de la
informática)
07/09/2010
Ing. Juan Manuel Cruz
4
1
Sistemas Embebido 2010 - C para Embebidos ARM
07/09/2010
Las Variables en Embebidos
„
Calificador de tipo “const”
Algunas palabras claves:
„
„
Calificador de tipo “const”
Declaración de variable en ROM y de puntero en
RAM que apunta a ROM
„
„
„
Calificador de tipo
p “volatile”
„
Representar periféricos mediante “structs”
„
Declaración de variable en RAM y de p
puntero en
ROM que apunta a RAM
„
„
07/09/2010
Ing. Juan Manuel Cruz
5
Calificador de tipo “const”
„
„
07/09/2010
„
La palabra clave de calificador de tipo se interpone
sintácticamente entre la clase del almacenamiento y
el identificador
„
static const int id;
Declara id como un identificador del tipo entero, con clase
de almacenamiento estática (inicializado una vez retiene su
valor al reentrar al bloque y su alcance se restringe al resto
del archivo fuente) calificada del tipo constante
07/09/2010
Ing. Juan Manuel Cruz
Ing. Juan Manuel Cruz
Ing. Juan Manuel Cruz
7
Declaración de variable en ROM y de puntero en
ROM que apunta a ROM
„
„
„
int id;
int * const ptr = &id;
Calificador de tipo “const”
Un calificador del tipo restringe la manera en que se
puede usar un identificador
„
static const int id = 4;
const int *ptr = &id;
6
const int id = 4;
const int * const ptr = &id;
Declaración de p
puntero en ROM q
que apunta
p
a
posición de memoria fija
„
int *const ptr = (int *)0x40000000;
07/09/2010
Ing. Juan Manuel Cruz
8
2
Sistemas Embebido 2010 - C para Embebidos ARM
07/09/2010
Representar periféricos
mediante “structs”
Calificador de tipo “volatile”
„
„
„
„
El calificador de tipo volátil indica que la variable puede
cambiar independiente del flujo del programa (típicamente por
eventos en el hardware del periférico del microcontrolador)
volatile int id;
Al calificar una variable como volátil el compilador no
optimizará las referencias a la misma asegurándonos que
mantendrá su valor inalterable desde la última asignación
(optimización que el compilador intentará hacer a variables de
otro tipo)
07/09/2010
Ing. Juan Manuel Cruz
9
„
07/09/2010
Es necesario declarar como volátil los registros de periféricos
de I/O cuando su contenido depende de eventos de hardware
„
Declaración de puntero a registro de periférico (posiciones de
memoria fijas)
„
volatile unsigned int *const ptr = (unsigned int *) 0xE0000000;
„
„
Ing. Juan Manuel Cruz
Ing. Juan Manuel Cruz
11
Se requieren funciones separadas de Set y Clear Bit pues los
pines de I/O y los valores de los registros de periféricos pueden
cambiar por eventos de hardware y sólo deberán escribirse los
bits que es necesario modificar. Esto puede hacerse teniendo la
capacidad de manipular bit. El puerto de I/O permite escribir un
valor del 32-bits de ser requerido
La declaración de estructura correspondiente es:
typedef struct
{ volatile unsigned int IOPIN;
volatile unsigned int IOSET;
volatile unsigned int IODIR;
volatile unsigned int IOCLR;
} GPIO;
Note que la variable puede calificarse simultáneamente como
volatile y const, indicando un registro cuyo cambios de valor
se deben a eventos del hardware y qué no debe asignarse
(registro del resultado de un conversor A/D)
07/09/2010
Ing. Juan Manuel Cruz
Representar periféricos
mediante “structs”
Calificador de tipo “volatile”
„
Los registros entrada salida de propósito general de
un puerto de 32 bits de un microcontrolador NXP
ARM son:
10
07/09/2010
Ing. Juan Manuel Cruz
12
3
Sistemas Embebido 2010 - C para Embebidos ARM
07/09/2010
Representar periféricos
mediante “structs”
„
GPIO es un tipo de estructura que se usa para declarar puertos.
Por ejemplo, el micro tiene 4 puertos declarados como:
„
„
Consideraciones para la depuración
GPIO Port0, Port1, Port2, Port3
Los punteros a Port0 pueden declararse como:
„
„
GPIO *const ptr_Port0 = (GPIO *) 0xE0028000; /* address of Port 0 */
ptr_Port0 es un puntero constante a una variable del tipo GPIO (estructura
que contiene 4 variables que representan puertos de I/O), apunta a la dirección
baja de la estructura (del puerto de I/O mapeado en memoria), la conversión a
puntero tipo GPIO es necesario para mantener la consistencia
Podemos usar el puntero para acceder al port0 de GPIO como:
„
ptr_Port0 -> IOSET = 0xD0D0FEED
Las otras variables pueden leerse o escribirse de manera similar
„
„
typedef struct {...} Volatile GPIO
Ing. Juan Manuel Cruz
13
Consideraciones para codificar
en C sobre procesadores ARM
„
„
Existen muchas consideraciones básicas de programación para
procesadores ARM, la documentación del compilador usado
aporta gran detalle sobre diversos aspectos importantes y
sutiles a considerar y debe leerse detenidamente (aspectos del
trabajo con la arquitectura y con el compilador ARM)
Es útil analizar el lenguaje assembly generado por el compilador
para aprender sobre la eficiencia y lo que hace el compilador
Ing. Juan Manuel Cruz
14
La eficiencia de depuración y de código a veces chocan. El
compilador da opciones “código vs info. de depuración” (aprox.
al 15%, uno debe ver sus efectos y usar la más apropiada)
De haber efectos secundarios el código que se ejecuta en modo
depuración no podrá ejecutarse en otros modos; es mejor estar
en modo de depuración para que el código que se ejecuta en el
sistema sea lo más parecido al código que se verificó
Al optimizar pueden surgir diferencias al código usado en la
depuración con efectos secundarios imprevistos. Si el modo de
depuración ofrece recursos (Semi-Hosting) no disponibles en su
sistema; se deberá quitar/reemplazar esas llamadas por código
propio (la ejecución condicional de ARM está deshabilitada para
todas las opciones de depuración)
07/09/2010
Ing. Juan Manuel Cruz
15
Consideraciones de Lazos
„
La consideraciones de codificación son muy importantes cuando
se procura optimizar el rendimiento y ocupar cantidades
li it d de
limitadas
d memoria
i embebida.
b bid El costo
t de
d desbordar
d b d una
configuración de memoria dada suele ser alto (saltar al chip
más próximo y de no existir forzaría el rediseño del sistema)
07/09/2010
Ing. Juan Manuel Cruz
„
La estructura GPIO completa también puede declararse volátil:
07/09/2010
„
„
„
Los programas embebidos siguen la regla 90/10: el 90% del
tiempo se ejecuta el 10% del código (Hennessy & Patterson).
Pasan la mayor parte del tiempo ejecutando Lazos (usualmente
saltos hacia atrás), por lo que es importante comprender y
optimizar comportamiento de lazos
Una simple optimización es escribir for de conteo regresivo que
terminen en cero, en lugar de contar hasta un valor máx.
„
„
for (i = 1; i < n; i++),
i++) el compilador genera una instr.
instr de comparación
entre i y n y luego una de salto por menor que cerrando el lazo
for (i=n; i != 0; i--), el compilador puede setear el flag de cero al hacer
el decremento (SUBS) y luego BNE finaliza el lazo; se ahorra tiempo,
tamaño de código y se libera un registro pues la variable n es un valor de
inicialización para el puntero del lazo en lugar de tener que mantenerse en
un registro a fin de compararla con la variable i en cada iteración del lazo
07/09/2010
Ing. Juan Manuel Cruz
16
4
Sistemas Embebido 2010 - C para Embebidos ARM
07/09/2010
Asignación de Registros y
Alias de Punteros
„
„
En ARM las variables pueden alocarse en registros en lugar de
memoria (con gran ahorro en rendimiento y tamaño de código)
Por la consistencia de valor de la variable, el compilador debe
asegurarse que el valor de la variable en memoria no cambia
por una referencia de puntero en otros puntos del programa
(pointer aliasing), por lo tanto una variable alocada en un
registro debe:
„
„
„
Tipos de Datos y
Alineación Natural en ARM
„
Ing. Juan Manuel Cruz
17
„
int prog1 (int var1)
{ somefunction (&var1);
El paso de la dirección de var1 y la posterior referencia a var1 en memoria
pues el compilador no puede garantizar que un valor de registro no se
cambiará. Sin embargo, mediante el uso de una variable intermedia local:
int prog2 (int var2)
{ int local1 = var2;
somefunction (&local1);
var2 = local1;
Toda referencia posterior a var2 se hará por registro ya que su dirección
no fue tomada, el compilador puede alocarla en un registro y accederla con
instrucciones MOVs en lugar de usar instrucciones de Stack, Load o Store
07/09/2010
Ing. Juan Manuel Cruz
07/09/2010
Ing. Juan Manuel Cruz
19
Tipos de Datos y
Alineación Natural en ARM
Las variables globales no pueden alocarse en registros pues sus
valores no son privados a un módulo determinado
Ejemplo de asignación de alias de puntero:
„
Ing. Juan Manuel Cruz
El código ARM puede acceder a datos más eficientemente si
corresponde a su tamaño natural, los límites de la alineación
para los tipos de variable C son:
Al crear un puntero a una variable el compilador no está seguro
si el valor de la misma no es cambiado desde fuera del módulo,
por lo tanto debe ubicar la variable en memoria (no en registro)
Asignación de Registros y
Alias de Punteros
„
Entre la arquitectura de un microcontrolador y los tipos de
variables en C allí pueden existir alguna alineación natural, por
ejemplo, la capacidad para manipular “naturalmente” los tipos
byte o word (en una sola instrucción y eficientemente)
a) Ser una variable local o un parámetro de función
b) No tienen su dirección tomada (& var_name) o asignada a otra variable
07/09/2010
„
„
18
„
„
„
Como ARM es una arquitectura de 32 bits, se espera que sea
más eficiente en el manejo de su tipo de tamaño "natural”
La arquitectura es más eficiente usando enteros pues ARMv4 y
derivadas pueden cargar valores con y sin signo de 8 y 16 bits
(aunque todos los registros ARM y la ALU son de 32-bits)
El tratamiento de no enteros como char y short es:
„
„
a)) Los
L valores
l
sin
i signo
i
se expanden
d con ceros
b) Los valores signados se expanden con signo
Por lo que toda variable char & short se deberá convertir después de cada
operación para comprobar si ha sobrepasado su límite, siendo mucho más
eficiente usar int para variables locales y convertir el valor retornado de ser
necesario (el uso de ints reduce tamaño de código y aumenta el rendimiento)
07/09/2010
Ing. Juan Manuel Cruz
20
5
Sistemas Embebido 2010 - C para Embebidos ARM
07/09/2010
Tipos de Datos y
Alineación Natural en ARM
„
ARM Thumb® Procedure Call
Standard (ATPCS)
Aquí hay un ejemplo de eficiencia utilizando el tipo int:
„
„
short somefunction (short i) { i = i + 1; return i; }
El código generado será:
ADD
a1, a1, #1
MOV
a1, a1, LSL #16 /* put sign bit in MSB */
MOV
a1, a1, ASR #16 /* sign extend */
MOV
pc, lr
/* return */
int somefunction (int i) {i = i +1; return i;}
El código generado es:
ADD
a1, a1, #1
MOV
pc, lr
/* return */
Use enteros cuando sea posible para evitar las conversiones de shorts & chars.
Como cada regla tiene excepciones puede haber un balance entre el tamaño del
código y del espacio variables
07/09/2010
Ing. Juan Manuel Cruz
21
ARM Thumb® Procedure Call
Standard (ATPCS)
„
„
Comprender y aprovechar las ventajas de esta convención
pueden conducir a programas más eficientes. En particular, se
incluyen los registros utilizados por el compilador:
„
„
„
Ing. Juan Manuel Cruz
Ing. Juan Manuel Cruz
„
23
Las implicancias de usar ATPCS
„
„
a) Registros r0 a r3 (a1 a a4) para pasar valores de parámetro a rutinas y
retornar valores. La rutina llamada no debe restaurarlos a su regreso
b) Registros r4 a r11 (v1 a v8) usados para variables locales (alocar
registros). La rutina llamada debe restaurar el estado de estos registros
antes de regresar (salvar en la pila & restaurar)
c) Hasta el registro 14 variables locales (r0 a r11, r12 y r14) de funciones
d) más variables locales producirán el vuelco a memoria (caro reemplazo de
accesos de registro muy eficiente) debe evitarse mientras se sea posible
07/09/2010
Ing. Juan Manuel Cruz
ARM Thumb® Procedure Call
Standard (ATPCS)
El compilador de C para ARM usa un convenio estándar para el
paso de parámetros y el uso de registro llamado ATPCS
„
07/09/2010
22
„
„
„
„
„
Algunas reglas simples al escribir funciones para obtener código más
pequeño y más rápido (procure reemplazar accesos a memoria externa por
registro internos). Las pautas simples para las funciones incluyen:
a) Pueden pasarse hasta 4 words como argumentos a función en registros
(r0 ..r3). Más de 4 argumentos se pasarán por la pila vía acceso a memoria
b) Las funciones con 4 o menos argumentos ahorran espacio del código y
tiempo de ejecución, limítese a funciones simples
c) Si hay más de 4 argumentos, agrúpelos en una estructura y pase un
puntero a la estructura (es muy ineficiente pasar una estructura)
d) Limite las variables locales a menos de 10 (para 4 argumentos)
e) Evite las funciones variadic con número variable de parámetros
f) Para retornar valores también se usan ro .. r3 (más valores se retornan
en la pila)
07/09/2010
Ing. Juan Manuel Cruz
24
6
Sistemas Embebido 2010 - C para Embebidos ARM
07/09/2010
Alineamiento de estructuras
y Uso de Memoria
„
„
„
„
„
_packed & Bit-Fields
El orden en q
que se declaran las variables en una estructura afecta el número de
bytes de almacenamiento requerido y el medio de acceso (el tipo de
instrucciones generada)
Para dos declaraciones de una estructura de 2 chars,
chars un short y un int:
struct {char c; short s; char d; int i;}
bytes: c x s s d x x x i i i i (12 bytes en total, 4 x: 4 bytes de relleno)
struct {char c; char d; short s; int i}
bytes: c d s s i i i i (8 bytes en total, 0 bytes de relleno pues los campos están
de acuerdo con el método de alineación del compilador). Puede ser provechoso
prestar atención al arreglo de estructuras para ahorrar bytes
Ing. Juan Manuel Cruz
25
_packed & Bit-Fields
La regla
g básica es que
q valores enteros se alinean naturalmente
con la arquitectura ARM de 32-bits y el pensamiento debe
entrar en usar variables no-alineadas más cortas para asegurar
que cualquier ganancia no se torne en un mayor procesamiento
y en accesos ineficientes
07/09/2010
„
a) Read Only Memory (ROM, flash): para código
„
b) RAM: para datos (heap, stack) o código
„
Con bit-fields
bit fields ocurre algo similar (se compacta a costa de más
código y más bajo rendimiento). Puede usarse en estructuras
para crear campos de longitud definida por el usuario. Pueden
ser contiguos mientras no excedan el tamaño básico que los
contiene (el tamaño de un contenedor entero es 32 bits)
Ing. Juan Manuel Cruz
Ing. Juan Manuel Cruz
27
El mapa de memoria representa la dirección de arranque y la
longitud de las diversas zonas de memoria que corresponde a:
Typedef _packed struct { char c; int i; short s; char d;}
07/09/2010
Ing. Juan Manuel Cruz
„
„
bytes: c i i i i s s d, comenzando a un límite de 4 u 8 bytes. Ahorran espacio
pero a los accesos a las variables no alineadas el compilador debe agregar
desplazamientos y uniones para formar al int i en un registro (muy ineficiente
pues crea más instrucciones y reduce el rendimiento). Debe justificarse su uso
„
„
struct bitter { int a:10; int b:20; int c:30;}
bits of Word 1: aaaaa.aaaaa.bbbbb.bbbbb.bbbbb.bbbbb.xx
bits of Word 2: ccccc.ccccc.ccccc.ccccc.ccccc.ccccc.xx
Los bits x son de relleno. Como los bit-fields residen en memoria su acceso
es mediante punteros, de acceso ineficiente y naturaleza no alineada (es
más efectivo el enmascaramiento lógico de variables enteras)
Mapa de memoria
Estructuras definidas con el calificador _packed
packed alinean sus
miembros a 1byte (sin ningún relleno)
„
Por ejemplo:
„
El compilador alinea la dirección de inicio del primer miembro de una estructura
a la longitud de acceso más grande (4 u 8 bytes) y luego alinea al resto a la
máxima alineación requerida por su longitud (un campo de tipo char se alinea al
próximo byte disponible mientras un tipo del int se alinea al próximo word),
insertando bytes de relleno en los huecos de alineación
07/09/2010
„
„
„
„
26
c) Periféricos: registros de periféricos mapeados en memoria (su
contenido se modifica por eventos de hardware y pueden haber requisitos
especiales de acceso)
d) Vectores de Interrupción: posiciones de memorias fijas para los
manejadores de interrupciones y excepciones
Estas áreas pueden ser re-mapeadas luego del booteo (para re-programar
la memoria Flash)
07/09/2010
Ing. Juan Manuel Cruz
28
7
Sistemas Embebido 2010 - C para Embebidos ARM
07/09/2010
Mapa de memoria
„
Mapa de memoria
El mapa de memoria representa la dirección de arranque y la
longitud de las diversas zonas de memoria que corresponde a:
„
a) Read Only Memory (ROM, flash): para código
„
b) RAM: para datos (heap, stack) o código
„
„
„
c) Periféricos: registros de periféricos mapeados en memoria (su
contenido se modifica por eventos de hardware y pueden haber requisitos
especiales de acceso)
d) Vectores de Interrupción: posiciones de memorias fijas para los
manejadores de interrupciones y excepciones
Estas áreas pueden ser re-mapeadas luego del booteo (para re-programar
la memoria Flash)
07/09/2010
Ing. Juan Manuel Cruz
29
Mapa de memoria
„
„
a) Bloque de Booteo de 8K ubicado en 2GB, maneja los eventos de reset
del sistema e incluye un programa monitor de depuración en tiempo real (al
tope de memoria interna para que no cambie de derivado en derivado)
„
„
„
b) Los vectores de la Interrupción son re-mapeados luego del booteo para
ser invocado una vez verificado que el código del usuario existe y es válido
(checksum); sino el boot loader intenta cargar un programa en flash
c) La distribución de memoria se presenta al linker vía un archivo especial
llamado scatter load (permite poner en memoria zonas complejas y ser
invocado por el compilador mediante directivas de assembler)
Las zonas de memoria poseen propiedades específicas incluyen:
„
„
Ing. Juan Manuel Cruz
„
El diseño de mapa de memoria se mantiene entre derivados permitiendo el
re-uso de software. La ubicación de las rutinas de servicio de interrupción y
las convenciones de la interrupción se mantienen tanto como sea posible
07/09/2010
Ing. Juan Manuel Cruz
31
Distribuir la imagen del ejecutable en zonas adecuadas de
memoria para aprovechar un mapa de memoria complejo,
controlar el orden de la carga de variables/módulos y relacionar
las propiedades de variables a zonas de memoria específicas
„
„
Ing. Juan Manuel Cruz
Distribución de carga
Algunas observaciones sobre el mapa de memoria del micro
Philips LPC2294:
„
07/09/2010
30
a) Sólo-Lectura (RO): Código y datos que sólo serán leídos, no modificados
b) Lectura-Escritura (RW); Código y datos que pueden leerse y reescribirse
c) Cero-Inicializado (ZI): Datos inicializados en Cero
Pueden convivir varias zonas con atributos diferentes. Puede
copiarse código y datos de ROM a RAM en tiempo de ejecución.
Los datos pueden ser inicializados en cero (variables statics),
etc. Deben setearse direcciones de Stack y heap y re-mapear
Vectores antes de la ejecución
07/09/2010
Ing. Juan Manuel Cruz
32
8
Sistemas Embebido 2010 - C para Embebidos ARM
07/09/2010
Consideraciones adicionales y
conclusiones
Distribución de carga
„
Para hacer todos el anterior se cera un archivo de distribución
de carga y es pasado al Linker:
„
„
„
„
„
El archivo contiene nombres de las zonas, atributos, direcciones de
comienzo y opcionalmente longitudes
Dentro de cada zona, pueden disponerse que áreas o secciones de
programas vengan primero (o último)
Pueden alocarse secciones en direcciones fijas para poder encontrar ciertos
tipos de información a una dirección dada del microcontrollador
(información de fabricación)
„
„
„
„
Dado el mapa de memoria para el Philips el producto de
LPC2294, un mismo (mismo) simple esparza el archivo de carga
aparecería como sigue:
07/09/2010
Ing. Juan Manuel Cruz
33
Distribución de carga
„
Evite la división siempre
p que
q p
posible (use
(
desplazamientos
p
si su core ARM no
posee división que el hardware)
Las instrucciones ARM son in-interrumpibles: ‘Load and Store Multiple' puede
aumentar mucho la latencia de la interrupción en sistemas de memoria lenta;
use un flag del compilador para limitar el número de registros guardado o
cargados en una instrucción
Las librerías de Tiempo Real pueden ser muy grandes; reemplace las funciones
normalmente usadas con sus propias funciones evitar arrastrar tanto código
Hay más consideraciones a tener en cuenta listadas en la documentación del
compilador (estudie el manual y los ejemplos de prueba). Con el compilador y
estudiando el assembly pueden surgir más consideraciones. Finalmente, una
gran cita atribuida a B. Kernighan (vía la página informativa de J. Ganssle):
“Depurar es el doble de duro que escribir el código en primer lugar. Por
consiguiente, si usted escribe el código tan hábilmente como le sea posible, por
definición usted no es lo bastante astuto como para ponerlo a punto".
07/09/2010
Ing. Juan Manuel Cruz
35
Referencias
FLASH_IMAGE
_
0X00000000
{ FLASH_Code_Data 0X00000000 0x10000
{ vectors.o (Vect, +FIRST)
; Interrupt & Exception Vectors
* (+RO)
; All Read-Only Code and Data
}
RAM_Code_Data 0X40000000 0x4000
{ * (+RW, +ZI)
; All Read-Write Code and Data
}
}
„
Representing
p
g a Microcontroller in C - Ata. R. Kahn
„
„
http://ing.de.soft1.googlepages.com/
C para MCS-51 – J. M. Cruz
Este archivo posiciona la imagen del código en cero absoluto, especifica que la
interrupción y vectores de la excepción se carguen primero y a continuación el
código y datos Read Only (ROM, Flash)
Los datos de lectura-escritura (RAM) se posiciona en 0X40000000 y se
especifica que todos ellos (lectura-escritura y cero-inicializado) se cargue allí
07/09/2010
Ing. Juan Manuel Cruz
Ing. Juan Manuel Cruz
34
07/09/2010
Ing. Juan Manuel Cruz
36
9

Documentos relacionados