¿Por qué mi MCU vuelve? (a la instrucción anterior)

0

No sé por qué, pero a veces veo el mismo comportamiento extraño en el entorno del depurador. Mira este código (función):

static void I2C_TransferConfig(I2C_HandleTypeDef *hi2c,  uint16_t DevAddress, uint8_t Size, uint32_t Mode, uint32_t Request)
{
  uint32_t tmpreg = 0;

  /* Check the parameters */
  assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance));
  assert_param(IS_TRANSFER_MODE(Mode));
  assert_param(IS_TRANSFER_REQUEST(Request));

  /* Get the CR2 register value */
  tmpreg = hi2c->Instance->CR2;

  /* clear tmpreg specific bits */
  tmpreg &= (uint32_t)~((uint32_t)(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP));

  /* update tmpreg */
  tmpreg |= (uint32_t)(((uint32_t)DevAddress & I2C_CR2_SADD) | (((uint32_t)Size << 16 ) & I2C_CR2_NBYTES) | \
            (uint32_t)Mode | (uint32_t)Request);

  /* update CR2 register */
  hi2c->Instance->CR2 = tmpreg;  
}

Y eche un vistazo a esta imagen de código en el entorno del depurador:

Cuando lo ejecuto, ¡al principio va a la línea 4057 y luego a la línea 4054! ¿Por qué?

Como puede ver, ambas líneas son solo varias variables + varios operandos. ¡Eso es todo!

    
pregunta Roh

1 respuesta

4

El estándar C especifica que las lecturas y escrituras de variables volátiles y las llamadas a las rutinas de la biblioteca se consideran "operaciones observables". Se requiere que un compilador produzca un ejecutable que produzca todas las operaciones observables en la secuencia que dirige el archivo de origen, pero el código generado cambia alrededor de cualquier otra cosa en el código que considere oportuno, siempre que todas las operaciones observables se realicen en la misma secuencia. .

Por ejemplo, si el código calcula un valor, realiza una operación observable que no hace uso del valor y luego realiza una que lo hace, el compilador puede decidir realizar la primera operación observable antes de calcular el valor necesario para la segunda . Dado el código como:

int x = y/z;
if (volatile1)
  volatile2 = x;

si el valor x nunca se usa después del código anterior, y si las variables y y z no son volátiles, un compilador podría decidir legítimamente que el código sería más eficiente que

if (volatile1)
{
  int x = y/z;
  volatile2 = x;
}

ya que en la situación en la que volatile1 es cero, se tarda menos tiempo en ejecutarse, y si volatile1 es distinto de cero, no demora más. Por supuesto, todo el propósito de realizar la tarea antes de probar volatile puede haber sido minimizar el tiempo transcurrido entre la lectura de volatile1 y la escritura de volatile2 , pero si ese es el caso, puede ser útil hacer algo como:

volatile int dummy_volatile;
#define USE_VALUE(x) (dummy_volatile = (int)(x))

y luego ...

int x = y/z;
USE_VALUE(x);
if (volatile1)
  volatile2 = x;

Eso aseguraría que una operación observable utilizando el valor computado se realizaría antes de la lectura observable de volatile1 . Algunos compiladores pueden ofrecer un medio para "utilizar de forma visible" un valor sin tener que almacenarlo en ningún lugar, pero no conozco ningún medio estándar para hacerlo.

    
respondido por el supercat

Lea otras preguntas en las etiquetas