ARM C — как поместить постоянные элементы во флэш-память?

#c #embedded #constants

#c #встроенный #константы

Вопрос:

У меня есть этот код

 class IO {
 public:       
    IO(LPC_GPIO_TypeDef* port, int pin) : _pin(pin), _port(port) {};        

    const int _pin;
    LPC_GPIO_TypeDef* const _port;


    void test() {
        LPC_GPIO0->FIOSET = 0;
    }

};

IO led1(LPC_GPIO0, 5);

int main() {
    led1.test();

    return 0;
}
  

Когда я его компилирую, я получаю

 text       data     bss     dec     hex  filename
656        0          8     664     298  lpc17xx
  

Я бы ожидал, что переменные const _port и _pin будут сохранены во флэш-памяти, поскольку они помечены как const, а значения инициализации известны во время компиляции, но они выделены в разделе .bss . Есть ли какой-нибудь способ заставить их находиться во флэш-памяти?

РЕДАКТИРОВАТЬ: я пробовал это:

 struct IO {

    LPC_GPIO_TypeDef* port;
    int pin;

    void test() const {
        //_port->FIOSET = _pin;

        LPC_GPIO0->FIOSET = 0;
    }

};

const IO led1 = {LPC_GPIO0, 5};

text       data     bss     dec     hex filename
520        0          0     520     208 lpc17xx
  

кажется, это помогает. Почему это не работает с классами?

Комментарии:

1. Просто попросите компоновщика сгенерировать файл карты, чтобы увидеть, где расположены IO::_port и IO::_pin.

2. У вас, вероятно, еще нет C 11? Потому constexpr что здесь это немного помогло бы.

3. @walmis: что вы здесь строите? Приложение? Драйвер? Какая ОС?

4. Я пытаюсь создать некоторые аппаратные абстракции на C , обычно я все делаю на C, хотя я играю на C . Это для микроконтроллера NXP lpc1756 cortex-m3. Нет ОС, только голое оборудование.

5. Что произойдет, если вы объявите их как статические const ?

Ответ №1:

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

Вероятно, вы могли бы достичь желаемого, используя класс шаблона, и передать порт / вывод в качестве аргументов шаблона, а не аргументов конструктора.

Это может зависеть от компилятора, но, по моему опыту, вам нужно объявить переменную как static const, чтобы принудительно перенести ее во Flash, но это не сработает для того, что вы пытаетесь сделать.

Ответ №2:

Используйте размещение new для создания экземпляра класса в определенной ячейке памяти:

 void * memPtr = 0x???????;
IO* ptrIO = new(memPtr) IO(LPC_GPIO0, 5);
  

Комментарии:

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

Ответ №3:

Это не работает с классами, потому что вы по существу инициализируете с const помощью вызова функции (ctor). Это похоже на const int foo = rand() at global scope: const , но не является целым постоянным выражением.

struct Код не вызывает никакого ctor, ни для struct самого себя, ни для какого-либо члена.

Ответ №4:

Я думаю, вы увидите, что это так. data Раздел пуст, что, вероятно, означает, что ваши константы были сохранены в text разделе. Текстовый раздел доступен только для чтения и вполне может находиться в исполняемом на месте ИЛИ ПЗУ в зависимости от того, как создаются и запускаются ваши образы ПЗУ. (Раздел bss не учитывается, поскольку он не содержит никаких данных как таковых, а скорее сообщает загрузчику, сколько дополнительной памяти требуется для 0 инициализированных переменных.)

Комментарии:

1. Странно, я пытался удалить const из этих элементов, но ничего не меняется, он по-прежнему выделяет одинаковое количество .text и .bss .

2. Константы наверняка были сохранены в text разделе, но при инициализации программы они копируются в элементы объекта, которые хранятся внутри bss .

Ответ №5:

Строка IO led1(LPC_GPIO0, 5); выполняет две вещи. Он сообщает компилятору выделить структуру чтения / записи длиной 8 байт, а затем вызвать функцию инициализации, а именно. конструктор для инициализации его в LPC_GPIO0 и 5 соответственно в доступной для записи статической памяти. Эта инициализация выполняется конструктором, который запускается перед вызовом вашей основной функции

Ваш второй пример не включает функцию инициализации, а именно. конструктор but представляет собой простую инициализацию неизменяемой памяти на `LPC_GPIO0 и 5 соответственно и поэтому может быть помещен непосредственно в текстовый раздел.

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