Безопасно ли пытаться (и не удается) записать в const на STM32? - PullRequest
0 голосов
/ 08 ноября 2018

Итак, мы экспериментируем с подходом к выполнению математической математики. Он встроен, поэтому память ограничена, и у нас будут большие матрицы, поэтому мы можем хранить некоторые из них во флэш-памяти, а не в ОЗУ.

Я написал матричную структуру, два массива (один const / flash и другой RAM) и функцию «изменить» и «получить». Одну матрицу я инициализирую данными ОЗУ, а другую матрицу инициализирую данными флеш-памяти, используя приведение от const * f32 к * f32.

Что я обнаружил, так это то, что когда я запускаю этот код на моем встроенном процессоре STM32, матрица ОЗУ модифицируется, и матрица, указывающая на данные флэш-памяти, просто не меняется (значение 12.0 не «принимает», значение остается 2,0).

(до изменения) a = 2, b = 2, (после изменения) c = 2, d = 12

Это приемлемое поведение, поскольку мы не будем пытаться изменять матрицы данных флэш-памяти, но если мы допустим ошибку, мы не хотим, чтобы она вылетала.

Если я запускаю тот же код на моем компьютере с Windows с Visual C ++, однако, я получаю «нарушение доступа» при попытке запустить приведенный ниже код, когда я пытаюсь изменить массив const до 12.0.

Неудивительно, что Windows будет возражать, но я бы хотел лучше понять разницу в поведении. Это похоже на архитектуру процессора. Безопасно ли на нашем STM32 позволить коду попытаться записать в const массив и не дать ему никакого эффекта? Или есть побочные эффекты или причины, чтобы этого избежать?

static const f32 constarray[9] = {1,2,3,1,2,3,1,2,3};
static f32 ramarray[9] = {1,2,3,1,2,3,1,2,3};

typedef struct {
     u16 rows;
     u16 cols;
     f32 * mat;
} matrix_versatile;

void modify_versatile_matrix(matrix_versatile * m, uint16_t r, uint16_t c, double new_value)
{
    m->mat[r * m->cols + c] = new_value;    
}

double get_versatile_matrix_value(matrix_versatile * m, uint16_t r, uint16_t c)
{
    return m->mat[r * m->cols + c];
}

double a;
double b;
double c;
double d;

int main(void)
{
    matrix_versatile matrix_with_const_data;
    matrix_versatile matrix_with_ram_data;

    matrix_with_const_data.cols = 3;
    matrix_with_const_data.rows = 3;
    matrix_with_const_data.mat = (f32 *) constarray;

    matrix_with_ram_data.cols = 3;
    matrix_with_ram_data.rows = 3;
    matrix_with_ram_data.mat = ramarray;

    a = get_versatile_matrix_value(&matrix_with_const_data, 1, 1);
    b = get_versatile_matrix_value(&matrix_with_ram_data, 1, 1);
    modify_versatile_matrix(&matrix_with_const_data, 1, 1, 12.0);
    modify_versatile_matrix(&matrix_with_ram_data, 1, 1, 12.0);
    c = get_versatile_matrix_value(&matrix_with_const_data, 1, 1);
    d = get_versatile_matrix_value(&matrix_with_ram_data, 1, 1);    

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Ответ может зависеть от серии (STM32F1, STM32F4, STM32L1 и т. Д.), Поскольку они имеют несколько разные контроллеры флэш-памяти.

Однажды я совершил ту же ошибку на STM32F429 и немного исследовал ее, поэтому я могу сказать, что произойдет на STM32F4.

Возможно ничего.

Флэш-память по умолчанию защищена, чтобы быть несколько устойчивой к такого рода программным ошибкам. Чтобы изменить флэш-память, необходимо записать определенные значения в регистр FLASH->KEYR. Если записано неправильное значение, флэш-память будет заблокирована до сброса, поэтому ничего страшного не произойдет, если программа не запишет 64 бита правильных значений. Не может быть никаких неожиданных прерываний, потому что этот ключ также защищен битом разрешения прерываний. При попытке установить некоторые биты ошибок в FLASH->SR, чтобы программа могла проверить это и предупредить пользователя (предпочтительно тестера).

Однако, если там есть какой-то код (например, загрузчик или записывающий что-то во флэш-память), который должен записать что-то во флэш-память, т.е. он разблокирует флэш-память с правильными ключами, то плохие вещи может случиться.

Если флэш-память остается разблокированной после предыдущей операции записи, то запись в ранее запрограммированную область изменит биты с 1 на 0, но не с 0 на 1. Это означает, что флэш-память будет содержать побитовое AND старого и вновь записанного значения.

Если неудачная попытка записи происходит первой, а затем разблокируется, то никакая законная операция записи или стирания не будет успешной, если только сначала не будут правильно очищены биты состояния.

Если предполагаемый и непреднамеренный доступ происходит с чередованием, например, в обработчиках прерываний все ставки отключены.


Даже если значения находятся в неизменяемой флэш-памяти, результат все равно может быть неожиданным. Считайте этот код

int foo(int *array) {
  array[0] = 1;
  array[1] = 3;
  array[2] = 5;
  return array[0];
}

Оптимизирующий компилятор может распознать, что возвращаемое значение всегда должно быть 1, и выдать код с этой целью. Или это может не произойти, и перезагрузите array[0] из того места, где оно хранится, возможно, значение отличается от флэш-памяти. Он может вести себя по-разному в сборках отладки и выпуска, или когда функция вызывается из разных мест, так как она может быть встроена по-разному.


Если указатель указывает на не отображенную область, ни ОЗУ, ни FLASH, ни какой-либо регистр отображения памяти, произойдет сбой , а так как обработчики ошибок по умолчанию содержат только бесконечный цикл, программа зависнет если на нем не установлен обработчик ошибок, который может справиться с ситуацией. Нет необходимости говорить, что перезапись случайных областей ОЗУ или регистров может привести к едва предсказуемому поведению.


UPDATE

Я пробовал ваш код на реальном оборудовании. Когда я запустил его дословно, компилятор (gcc-arm-none-eabi-7-2018-q2-update -O3 -lto) оптимизировал все, так как переменные впоследствии не использовались. Пометка a, b, c, d как volatile привела к c=2 и d=12, он все еще рассматривал первый массив const, и доступ к массивам не был создан. constarray вообще не отображался в файле карты, компоновщик полностью его исключил.

Итак, я попробовал несколько вещей по одному, чтобы заставить оптимизатор генерировать код, который фактически получал бы доступ к массивам.

  • Отключить оптимизацию (-O0)
  • Создание всех переменных volatile
  • Вставка пары барьеров памяти времени компиляции (asm volatile("":::"memory");
  • Выполнение сложных вычислений в середине

Любой из них оказывал различное влияние на разные MCU, но они всегда были согласованы на одной платформе.

  • STM32F103: неисправность. Только полугодовое (16-битное) обращение к записи разрешено для флэш-памяти, 8 или 32 бита всегда приводят к ошибке. Когда я изменил типы данных на short, код работал, конечно, без какого-либо влияния на флэш-память.
  • STM32F417: выполнение кода без влияния на содержимое флэш-памяти, но биты 6 и 7, PGPERR и PGSERR в FLASH->SR были установлены через несколько циклов после первой записи попытка constarray.
  • STM32L151: выполнение кода без влияния на состояние контроллера флэш-памяти.
0 голосов
/ 08 ноября 2018

но если мы допустим ошибку, мы не хотим, чтобы она вылетела.

Попытка записи в ПЗУ сама по себе не приведет к сбою, но код, пытающийся записать ее, по определению содержит ошибки и может привести к сбою в любом случае, и, конечно, не будет вести себя как задумано.

Это почти совершенно неправильное мышление; если у вас есть ошибка, вы действительно хотите, чтобы она вылетала во время разработки, а не после развертывания. Если он молча делает что-то не так, вы можете никогда не заметить ошибку, или сбой может произойти где-то, кроме близости ошибки, поэтому очень трудно найти.

Архитектура MMU или MPU может выдавать исключение, если вы пытаетесь записать в память, помеченную как доступную только для чтения. Это то, что происходит в Windows. В этом случае это может быть полезным средством отладки с учетом обработчика исключений, который каким-либо образом сообщает о таких ошибках. В этом случае ошибка сообщается именно тогда, когда она возникает, а не происходит сбой через некоторое время, когда происходит доступ к некоторым недопустимым данным или к ошибочным результатам.

Некоторые, но не все детали STM32 включают MPU ( Замечание по применению )

...