¿Por qué la configuración de algunos GPIO cuesta tanta memoria?

2

Estoy tratando de escribir una biblioteca para mi afición y está casi lista para una prueba de humo. Lo único que me molesta es una función llamada una vez:

(Nota: todas las frases en mayúsculas son constantes de macro)

{
/////////// Interface clock enable
LCD_CTRL_PORT_CLK_EN;
LCD_DATA_PORT_CLK_EN;

//////////// Config control pins
//// pull up
LCD_CTRL_PORT->PUPDR &= ~LCD_CTRL_PORT_32BIT_MASK;
LCD_CTRL_PORT->PUPDR |= ( (0b01 << (LCD_CS_POS*2U))
                        | (0b01 << (LCD_RS_POS*2U))
                        | (0b01 << (LCD_WR_POS*2U))
                        | (0b01 << (LCD_RD_POS*2U))
                        | (0b01 << (LCD_RST_POS*2U)) );
//// set output direction
LCD_CTRL_PORT->MODER &= ~LCD_CTRL_PORT_32BIT_MASK;
LCD_CTRL_PORT->MODER |= ( (0b01 << (LCD_CS_POS*2U))
                        | (0b01 << (LCD_RS_POS*2U))
                        | (0b01 << (LCD_WR_POS*2U))
                        | (0b01 << (LCD_RD_POS*2U))
                        | (0b01 << (LCD_RST_POS*2U)) );
//// Max output speed
LCD_CTRL_PORT->OSPEEDR &= ~LCD_CTRL_PORT_32BIT_MASK;
LCD_CTRL_PORT->OSPEEDR |= ( (0b11 << (LCD_CS_POS*2U))
                            | (0b11 << (LCD_RS_POS*2U))
                            | (0b11 << (LCD_WR_POS*2U))
                            | (0b11 << (LCD_RD_POS*2U))
                            | (0b11 << (LCD_RST_POS*2U)) );
//// all control pin set HIGH (idle)
LCD_CTRL_PORT->BSRR |= (LCD_CS | LCD_RS | LCD_WR | LCD_RD | LCD_RST);
////////

//////////// Config data pins
LCD_DATA_PORT->PUPDR &= ~LCD_CTRL_PORT_32BIT_MASK;
LCD_DATA_PORT->OSPEEDR &= ~LCD_DATA_PORT_32BIT_MASK;
LCD_DATA_PORT->MODER &= ~LCD_CTRL_PORT_32BIT_MASK;

#if LCD_USE8BIT_MODE == 1
LCD_DATA_PORT->PUPDR |= ( 0x5555 << (LCD_DATAPORT_OUTPUT_OFFSET*2U) );
LCD_DATA_PORT->OSPEEDR |= ( 0xFFFF << (LCD_DATAPORT_OUTPUT_OFFSET*2U) );
LCD_DATA_PORT->MODER |= ( 0x5555 << (LCD_DATAPORT_OUTPUT_OFFSET*2U) );
LCD_DATA_PORT->BSRR = ( 0xFF << (LCD_DATAPORT_OUTPUT_OFFSET*2U) );
#else
LCD_DATA_PORT->PUPDR |= 0x55555555;
LCD_DATA_PORT->OSPEEDR |= 0xFFFFFFFF;
LCD_DATA_PORT->MODER |= 0x55555555;
LCD_DATA_PORT->BSRR = 0xFFFF;
#endif

Estoy usando True Studio y la plataforma es STM32F3. La función simplemente modifica claramente algunos registros con constantes derivadas de algunas constantes macro.

¿Pero por qué cuesta casi 1 kB de flash (en la sección .text, estoy usando el analizador de compilación de Truestudio) aunque no es muy largo? Incluso la función HAL-GPIO es 3 veces más pequeña.

Ejemplo de macro:

#define LCD_XXXX_PORT  GPIOX
#define LCD_XXX        GPIO_PIN_X
#define LCD_XXX_POS    POSITION_VAL(LCD_XXX)
    
pregunta Long Pham

3 respuestas

2

Creo que he encontrado dos razones:

1. El compilador no es lo suficientemente inteligente como para reconocer algunas constantes de tiempo de compilación

El problema radica en estas macros: #define LCD_XXX_POS POSITION_VAL(LCD_XXX)

Quiero crear una máscara automática para varios registros de configuración GPIO. Para hacerlo, al menos debo saber la posición de cada pin. Así que uso POSITION_VAL() , una función de macro provista en el encabezado CMSIS de ST:

#define POSITION_VAL(VAL)     (__CLZ(__RBIT(VAL))) 

Básicamente, la función macro toma un parámetro (en este caso, máscara de pin GPIO único), la invierte con la instrucción rbit , calcula los ceros iniciales con la instrucción clz y devuelve los ceros iniciales. Al hacerlo, puedo obtener la posición del pin LCD_XXX con bastante facilidad.

El compilador parece reemplazar la función macro con su cuerpo cada vez, en lugar de reemplazarlos con constantes. He probado al definir LCD_XXX_POS como constantes, el tamaño de la función se redujo a ~ 380 bytes.

2. Algo estaba mal con la configuración del proyecto

Sobre la macro POSITION_VAL(VAL) , el compilador puede no (¿no?) preocuparse si se alimenta con constantes. Entonces, como lo sugiere este artículo , he creado un nuevo encabezado:

#ifdef POSITION_VAL
#undef POSITION_VAL
#endif

#ifdef __GNUC__
# define POSITION_VAL(VAL) (__builtin_ctz(VAL))
#else
# define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL)))
#endif
  

GNU C tiene __builtin_ctz que logra lo mismo pero puede deducir el valor en el momento de la compilación si es apropiado.

Pero Truestudio resaltó #ifdef __GNUC__ rama como inactiva, aunque estaba seguro de que estaba usando GCC. Probé con una nueva plantilla de proyecto, la rama #ifdef __GNUC__ no se marcó como inactiva y la función solo cuesta ~ 180 bytes .

Claramente, había algo mal .....

    
respondido por el Long Pham
2
  

¿Pero por qué cuesta casi 1 kB de flash aunque no es muy largo? Incluso la función HAL-GPIO es 3 veces más pequeña.

Primero veamos lo que pediste.

9 |= de operaciones con un literal *
6 &= ~ operación con un literal *
Operación de 1 = con literal *

* asumiendo que el compilador optimiza la aritmética en las constantes en -O1

El |= toma 4 instrucciones (2 LDR, ORR y STR), y dos constantes. (18 bytes)
&=~ es similar a |= , ya que ~const se optimizará.
Un = son tres instrucciones (LDR, MOV y STR) y dos constantes. (10 bytes)

Como los registros son volatile , estas asignaciones no se optimizarán más. Esto significa que el código anterior es de unos 280 bytes. Tal vez menos, sobre si más de -O1 manteniendo algunos punteros en r1.

Sin embargo, un proyecto ARM vacío también tiene aproximadamente 600 bytes.
Esa es solo la tabla de vectores (~ 80 palabras), y la pila y la inicialización del cero de memoria.

1kB, para " simplemente gpio " no es grande.

    
respondido por el Jeroen3
1

Podría deberse a que está utilizando la biblioteca de periféricos STM32F3. Si solo necesita acceso GPIO, puede incluir stm32f3xx.h directamente. Elimine USE_STDPERIPH_DRIVER de la configuración de compilación.

    
respondido por el Maple

Lea otras preguntas en las etiquetas