Перейти к содержанию
    

И всётаки вопрос: Если применяешь с компиллер, то оптимизация описанным путём, - это же явная привязка к конкретному компилятору. И даже, вероятней всего, к его конкретной версии. :)

вопроса-то вроде и нет, Вы сами все верно сказали. ;)

 

Еще дополню относительно третьего вопроса..

Третий. Насколько сложно программу написанную на IAR "заставить" компилится на GCC. Может у кого есть спец. ".h" файлы.

Зависит от привязки к компилятору. Чем больше текста написано на plain C, без использования прагм, ключевых слов характерных для конкретного компилятора, тем проще будет "заставить" код компилироваться другим компилятором. Хотя с ключевыми словами просто - можно создать хедер с соответствеющими макросами и все (пример osnwt).. А прагм не так уж много.. ;>

Существенное отличие между IAR и GCC на мой взгляд заключается наверное в том, что GCC основан на стандарте C99 и позволяет определять переменные только в начале функций, т.о. выражения вида for(int i; i=0;....}, прекрасно воспринимаемые компилятором IAR, в gcc компилироваться не будут. Соответственно лучше сразу ими не пользоваться и в IAR...

 

Мне для впихивания навороченного USB крипто-загрузчика в 4 килобайта бут-блока меги32 пришлось соптимизировать примерно 200 лишних байтов. Ясное дело, что не только таким способом - многое пришлось переписать по другому. Но результат был достигнут.

Я бы "навороченный" криптозагрузчик писал бы полностью на ассемблере, если бы мне потребовался действительно небольшой объем. А так (с упованием на оптимизацию компилятора C) могу только сказать "овчинка выделки не стоит". Появится новая версия компилятора и код, вполне возможно, уже не втиснется в 4k.

Изменено пользователем defunct

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я бы "навороченный" криптозагрузчик писал бы полностью на ассемблере, если бы мне потребовался действительно небольшой объем. А так (с упованием на оптимизацию компилятора C) могу только сказать "овчинка выделки не стоит". Появится новая версия компилятора и код, вполне возможно, уже не втиснется в 4k.

Дело в том, что 2 килобайта - это USB драйвер, еще килобайт - это дешифровщик. Тем самым мы уже вылезли за пределы 2Кбайт. Остальное - это протокольная часть со всякими штуками.

 

Естественно, что можно все написать на асме, если писать с нуля. Но переписывать 3/4 кода просто из спортивного интереса я не могу себе позволить, поскольку это не моя работа, а всего лишь хобби. Я предпочту уделить это время написанию собственно того, что можно загружать этим загрузчиком, к примеру.

 

Кроме того, бут - это такая штука, которая едва ли будет часто меняться или развиваться. Обычно базовых функций хватает. Скорее, снимут с производства AVR в пользу чего-то другого, чем потребуется что-то менять. Новый компилятор едва ли будет давать бОльший код, чем старый. При этом нормальная практика в серьезных проектах - хранить средства разработки под версионным контролем наряду с собственно разработкой. Так что всегда есть возможность откомпилироваться старым.

 

Иными словами, все определяется потребностями и возможностями. В рамках имеющихся потребностей мне хватило имеющихся возможностей реализовать желаемое с минимальными затратами времени (а переписывание загрузчика или USB драйвера займет на порядок-два больше усилий на отладку и поиск трудноуловимых глюков при малейшей ошибке). В другой ситуации будет лучше все писать с нуля и на асме, но не в этой. Естественно, это всё IMHO.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Кроме того, бут - это такая штука, которая едва ли будет часто меняться или развиваться. Обычно базовых функций хватает. Скорее, снимут с производства AVR в пользу чего-то другого, чем потребуется что-то менять. Новый компилятор едва ли будет давать бОльший код, чем старый. При этом нормальная практика в серьезных проектах - хранить средства разработки под версионным контролем наряду с собственно разработкой. Так что всегда есть возможность откомпилироваться старым.

Вот именно, и это как раз тот случай когда применение ассемблера действительно оправдано!

 

 

Дело в том, что 2 килобайта - это USB драйвер

....

В другой ситуации будет лучше все писать с нуля и на асме, но не в этой. Естественно, это всё IMHO.

Возможно, а возможно было бы целесообразно применить код Igor Cesko о котором Вы упоминали...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 osnwt

Как пояснил Кристиан, gcc не рассчитывался на гарвардскую архитектуру процессора. Потому чтобы поддержать в нем AVR, пришлось сделать немалые хаки.

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

Тем более гарвардских процессоров добрая половина.

Интересно сравнить например с возможностями SDCC.

 

2defunct

Существенное отличие между IAR и GCC на мой взгляд заключается наверное в том, что GCC основан на стандарте C99 и позволяет определять переменные только в начале функций, т.о. выражения вида for(int i; i=0;....}, прекрасно воспринимаемые компилятором IAR, в gcc компилироваться не будут. Соответственно лучше сразу ими не пользоваться и в IAR...

 

Ну во-первых обьявления в операторе for это как раз С99,

а во-вторых gcc поддерживает 4 стандарта С89-С99 и их расширения для GNU, тут как раз проблемы нет - С99 и вперед.

 

2SasaVitebsk

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

По ссылке ниже - анализ компиляторов с примерами - примените их к AVRу и получите представление о сходстве и различии в их поведении.

http://www.rsdn.ru/article/devtools/devtools.xml

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Вот именно, и это как раз тот случай когда применение ассемблера действительно оправдано!

Если бы у меня стояла задача минимизировать цену в большой партии устройств, то я бы, возможно, и попытался. Однако, код USB занимает больше 1.5 килобайт. Втиснуть в оставшееся дешифровальщик и протокольную часть было бы проблематично, хотя шанс и есть. Но в моей ситуации это была бы неоправданная трата времени.

 

Дело в том, что 2 килобайта - это USB драйвер

....

В другой ситуации будет лучше все писать с нуля и на асме, но не в этой. Естественно, это всё IMHO.

Возможно, а возможно было бы целесообразно применить код Igor Cesko о котором Вы упоминали...

 

Цитата с сайта автора avr-usb (выделено мною):

 

Advantages over other Firmware-Only Implementations

 

A similar driver for the AVR series of microcontrollers is available from Igor Češko. Our USB driver has the following advantages over Igor's driver:

 

* All customizable code written in ANSI-C and thus easier to maintain.

* Modular concept: easier to integrate into existing designs.

* Slightly smaller code size in spite of high level language modules.

* Faster: All encoding/decoding (USB requires NRZI coding and bit stuffing) is done in real-time, not in the main loop after storing away the raw data stream.

* AVR-USB comes with a free shared Vendor- / Product-ID pair.

* The level of standards conformance is documented (description of limitations and potential problems).

* Available for free with Open Source license. (See the file License.txt in the distribution.)

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

 

 

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

Не вижу ничего странного. При различных методах адресации кода и данных нужно помнить разные типы указателей или использовать нечто вроде generic типа указателя в IAR, который крайне неэффективен. Доступ к данным тоже осуществляется совершенно по разному. Вроде, мелочь - но, видимо, существенная.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Похоже даже максимального уровня оптимизации мне будет не достаточно. :)

То что я увидел в обработчике самого быстрого прерывания (245мкс), трудно назвать программой в здравом уме. Читал некоторые посты по оптимизации. Применил всё что я вычитал оттуда. Также захотел "облегчить работу компилятора" заменив ряд логических операций, - таблицей перекодировки.

Теперь даже не знаю что былобы лучше.

И ещё скажите мне почему компилятор так упорно применяет команды типа "LDD R18, Z+61" вместо "lds r18,имя". Явно нагляднее последнее. (В Z компилятор заносит имя переменной вообще не имеющей отношения к процедуре прерывания). Учитывая использование данной инструкции, похоже необходимо переменные используемые в одном месте группировать? Может есть у кого рекомендации по оптимизации программы на С? Я пока чётко понял одну касающуюся необходимости правильного размещения или объявления процедуры прерывания.

Вопросов у меня несколько.

1) Как правильно объявить процедуру прерывания если я её полностью напишу на ASMе? Достаточно ли просто extern?

2) Где почитать по макросам для IAR ASM?

3) На объявление переменной типа:

 

struct

{

uint8_t RXOFF : 1, // Буфер переполнен, загрузка приостановлена

RMaster: 1, // Контроллер в режиме "Мастер"

RLoadActKom : 1, // Разрешена Загрузка команд в "активную зону"

cursiv: 1, // вывод текста курсивом

Color: 1, // цвет

Load485: 1, // Загрузка команд идёт с RS485

ErrLoadKom: 1; // Ошибка загрузки команды

// RActivPass: 1; // Прерывание завершено;

} Flag;

 

Компилятор выделил 11 (!) байт данных. Где у меня ошибка? Или я не правильно понял "возможность работы с битами" и эта "возможность" такая же как "boolean" под Паскалем?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

почему компилятор так упорно применяет команды типа "LDD R18, Z+61" вместо "lds r18,имя".

Обычно такое случаеться при работе со сложными типами данных.

В Z - адрес, а потом ездит по смещению.

Компилятор выделил 11 (!) байт данных. Где у меня ошибка? Или я не правильно понял "возможность работы с битами"

Есть две важных установки компилятора

1.Упаковка структур - т.е без выравнивания по адресам.

2.Разрешение работы с битовыми полями.

Устанавливаеться прагмами или в свойствах проекта.

 

Если такие проблемы - м.б. взять камень побыстрее?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И ещё скажите мне почему компилятор так упорно применяет команды типа "LDD R18, Z+61" вместо "lds r18,имя". Явно нагляднее последнее. (В Z компилятор заносит имя переменной вообще не имеющей отношения к процедуре прерывания).

 

LDS занимает 2 слова, а LDD - одно, при одинаковом времени выполнения. Очевидно, второй вариант предпочтительнее.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

То что я увидел в обработчике самого быстрого прерывания (245мкс), трудно назвать программой в здравом уме. Читал некоторые посты по оптимизации.

Основной совет: минимизировать количество операций в прерывании, ограничившись, например, выставлением флага или записью байта в регистр.

 

Битовые флаги удобно делать регистровыми типа __regvar. Экономит сохранение регистров, да и все остальное.

 

Не менее важный совет: избегать вызова функций из процедуры обработки прерывания. Если последняя работает автономно со своими локальными или глобальными переменными, то код будет самым эффективным. Если она вызывает хоть одну другую функцию, определенную в этом же файле - мы нарываемся на сохранение и восстановление некоторых регистров, используемых той функцией. Если процедура обработки прерывания вызовет хоть одну функцию типа extern, описанную в другом файле или непродекларированную - будьте готовы к полному сохранению и восстановлению половины регистров (все scratch регистры, которые любая C функция имеет право портить).

 

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

 

1) Как правильно объявить процедуру прерывания если я её полностью напишу на ASMе? Достаточно ли просто extern?

Вероятно, не extern, а public. Но ее вообще не нужно нигде объявлять, если это процедура обработки прерывания. C-компилятору вообще нет дела до этой процедуры, он ее не вызывает. Но при этом придется самому позаботиться о том, чтобы правильно разместить команду перехода на начало функции в таблице векторов прерываний. Пример можно посмотреть в исходных текстах USB драйвера. Там именно так и сделано.

 

2) Где почитать по макросам для IAR ASM?

Вероятно, в прилагаемой к нему документации?

 

Дополнительно можно посмотреть в его *.h файлы. Можно найти кое-что полезное.

 

3) На объявление переменной типа:

...

Компилятор выделил 11 (!) байт данных.

Как это было определено? С трудом себе это представляю. У меня, как и ожидается, 1 байт. Листинг ниже. А что у Вас в листинге?

 

//   87 __root struct
//   88 {
//   89  uint8_t  RXOFF   : 1, // Буфер переполнен, загрузка приостановлена
//   90              RMaster: 1, // Контроллер в режиме "Мастер"
//   91              RLoadActKom : 1, // Разрешена Загрузка команд в "активную зону"
//   92              cursiv: 1, // вывод текста курсивом
//   93              Color: 1, // цвет
//   94              Load485: 1, // Загрузка команд идёт с RS485
//   95              ErrLoadKom: 1; // Ошибка загрузки команды
//   96 // RActivPass: 1; // Прерывание завершено;

        RSEG NEAR_Z:DATA:ROOT(0)
        REQUIRE `?<Segment init: NEAR_Z>`
//   97  } Flag;
Flag:
        DS 1

(__root - это чтобы компилятор не выкинул переменную, на которую нет ссылок в коде).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У IAR синтаксис обычный для языка С. Т.е описание библиотек ты можешь найти в любой книжке по С. Приведенные Вами примеры функций - это обработчики прерываний. Они бодробно описаны в описании IAR. Как строить программу на C можно прочитать в любом самоучителе по С

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У IAR синтаксис обычный для языка С. Т.е описание библиотек ты можешь найти в любой книжке по С. Приведенные Вами примеры функций - это обработчики прерываний. Они бодробно описаны в описании IAR. Как строить программу на C можно прочитать в любом самоучителе по С

Вопросы были не в том, как строить программу на C, а специфических особенностях IAR. В том числе:

 

- как описать процедуру обработки прерывания на ассемблере (а не как построить программу на C).

Ответ: стандартно, так как C до нее нет никакого дела. Читать документацию по ассемблеру (именно по IAR ассемблеру - важно, как разместить rjmp по адресу вектора прерывания с использованием команд управления сегментами *SEG и счетчиком адреса ORG, которые частично отличаются по синтаксису от других ассемблеров).

 

- где почитать по макросам IAR ассемблера (а не про стандартные C библиотеки).

Ответ: после чтения документации полезно посмотреть не стандартные, а именно специфические для IAR *.h файлы, поскольку они используются и для C-программ, и для ассемблерных программ. Ассемблер IAR отличается от ассемблера Atmel синтаксисом большинства директив, а также использованием препроцессора C по умолчанию. Стандартные библиотеки C тут не причем.

 

- почему стандартное битовое поле из 8-ми битов превращается в 11 байтов вместо 1.

Ответ: и на самом деле, почему? Видимо, не так смотрелось. Нужно смотреть на листинг, во что превращается объявление. Этого ни при каком выравнивании не может быть, так как 8 байтов из 8 битов - еще понять можно, но 11...

 

То есть, даны ответы, в общем-то, правильные, но совсем не на заданные вопросы...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я извиняюсь перед всеми. Надо было более детально разобраться в произошедшем, а потом писать. Спасибо всем высказавшимся. :)

Скажу что изучал когда-то очень давно теорию компиляторов и ОС. :) Но тогда мне это было не нужно и безинтересно. Тогда безгранично годсподствовала ОС ЕС ЭВМ и только0-только появилась СВМ! С каким бы я интересом послушил бы сейчас того препода! Такой класный был преподаватель ...

 

Теперь по сути. Компилятор, очевидно, очень хороший. Местами великолепно "угадывает" как надо делать и прекрассно компилирует. Но некоторые фичи я пока не понимаю.

1) В процедуре обработки прерывания я не вызываю функций. (Прочитал в других ветках форума). Но если я пользуюсь переменными, объявленными как статические глобальные (вверху файла), то код получается значительно больший. Похоже компилятор пытается выделять локальные экземпляры переменных или что-то в этом духе. Обращение к этим переменным осуществляется ч/з указатель. Не совсем понимаю зачем это? Может чтобы дважды можно было вызвать? Если объявляешь статическими внутри функции, то код намного понятнее. А как надо объявлять? Я хочу чтобы от вызова к вызову переменные сохраняли своё значение.

2) Наблюдаю картину, что если арифметическое выражение сделать сложным, то компилятор начинает генерить сложный код. Получается что лучше разбивать операторы на более простые?

3) Мне необходимо в прерывании выполнить определённую операцию 18 раз. Можно сделать её путём логических операций. Но что бы ускорить процесс я решил воспользоваться табличным методом. Для этого сгенерировал соответсвующий массив. Вне данного прерывания этот массив использоваться не будет. Я объявил его в другом файле вместе с другими таблицами а в перывании объявил его как внешний. (Пробовал объявить его внутри прерывания, - компилятор ругается). Так вот обращение к этому массиву какое-то мудрёное. Может кто подскажет в чём дело?

4) На счёт одинадцати байт. :) И правда и не совсем. :) С одной стороны при обращении к битам происходит обращение только к одному байту! Причём обращение красивое!!! Но в распечатке листинга (релиз) я сейчас вижу следующее:

// 70 } Flag;

Flag:

DS 1

DS 1

 

А раньше выделялось 11 байт. Вроде ничего не менял. Пробовал воткнуть STATIC, - не получилось.

Кстати ко многим переменным добавляется ещё один байт. Это зачем?

 

Пятое и пока последнее. Спасибо всем откликающимся. Я конечно постепенно приду к определённым результатам и самостоятельно, не отрывая Вас от Ваших дел, но мне кажется что данный форум читаю не только я, но и другие начинающие программисты С. И, мне хочется надеятся, что он будет полезен для многих, поскольку я пытаюсь ставить вопросы, так сказать, из жизни.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...

Но некоторые фичи я пока не понимаю.

1) В процедуре обработки прерывания я не вызываю функций. (Прочитал в других ветках форума). Но если я пользуюсь переменными, объявленными как статические глобальные (вверху файла), то код получается значительно больший. Похоже компилятор пытается выделять локальные экземпляры переменных или что-то в этом духе. Обращение к этим переменным осуществляется ч/з указатель. Не совсем понимаю зачем это? Может чтобы дважды можно было вызвать? Если объявляешь статическими внутри функции, то код намного понятнее. А как надо объявлять? Я хочу чтобы от вызова к вызову переменные сохраняли своё значение.

...

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Компилятор в любом случае помещает эти переменные в SRAM. Но в первом описанном случае помещает ещё и указатели на эти переменные. Ну и обращается к ним ... соответственно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Ответ большей частью верный, но непонятный для начинающих. Попробую пояснить.

 

Имеем три вида переменных в языке C:

 

1) Глобальные, объявленные на внешнем уровне.

2) Локальные статические, объявленные внутри функции и сохраняющие свои значения между вызовами.

3) Локальные автоматические, объявленные внутри функции и не сохраняющие своих значений.

 

Переменные (1) компилятор размещает в SRAM. Слово register конкретным компилятором для глобальных переменных не будет удовлетворено, так как неизвестно, что с этими регистрами сделают другие функции. Для размещения глобальной переменной в регистре есть опция конкретного компилятора lock registers и ключевое слово __regvar (см. документацию). Залочив несколько регистров (то есть, зарезервировав их под статические переменные), вся программа и используемые библиотеки должна быть откомпилирована с такой же опцией, иначе на стадии линковки будет ошибка. Есть еще вариант размещения констант в памяти программ - const __flash, но это не относится к изменяемым переменным.

 

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

 

Локальные статические переменные (2) компилятор также размещает в SRAM (из ответа можно понять, что нет). Отличие от (1) - только в области видимости (конкретная функция). Все справедливо и в отношении игнорирования слова register. __regvar использовать, скорее всего, тоже не удастся: насколько помню, компилятор допускает его применение только для глобальных переменных. Почему обращение к локальным static менее эффективно, чем к глобальным - не скажу, не видя фрагмента кода.

 

Локальные автоматические переменные (3) - вот тут возможны варианты. Классически эти переменные размещаются в стеке, расположенном тоже в SRAM. Однако в данном случае компилятор и без всяких слов register будет пытаться разместить максимум автоматических переменных в регистрах. Сохранять их между вызовами не нужно, а есть достаточное количество регистров, которые функция может использовать и не обязана сохранять (см. документацию). Работа с регистрами обычно эффективнее, это верно. Но использовать минимум глобальных переменных рекомендуют вовсе не по этой причине, а из соображений стиля программирования и читабельности программ. Остальное более-менее вторично и зависит от конкретных особенностей компилятора.

 

Тут есть много нюансов. Например, единственный вызов функции без параметров, занимающий слово (2 байта) может очень неслабо увеличить размер некоей функции. Почему? А потому что вызванная функция может использовать половину регистров, и весь выигрыш от размещения локальных auto переменных в регистрах будет съеден необходимостью их сохранить перед этим вызовом, а потом восстановить.

 

Почему компилятор использует обращение через "указатель"? Ряд команд специально сделан для работы с индексированными данными. Например, в регистровую пару Z может быть загружен начальный адрес структуры, а потом к ее членам можно обращаться одной командой через Z+смещение. Компактно и удобно. Этот прием компилятор может использовать и для доступа к локальным переменным, и для глобальных с целью экономии кода.

 

Насчет сложного кода в выражениях: заочно ответить нельзя. В таких случаях надо писать иначе: вот фрагмент 1 (кусок кода), вот фрагмент 2 (кусок кода). Первый занимает на 20 байтов больше. В чем причина? Иногда ответить на этот вопрос вообще нельзя, не видя описания переменных и т.п. Часто просто удобнее писать по частям, заводя по ходу дела промежуточные переменные. Не следует беспокоиться: при высоких уровнях оптимизации компилятор так или иначе не будет хранить то, что дальше по тексту не требуется. А читабельность это может поднять.

 

Про массивы в прерывании. То же самое. Не видя кода, нельзя сказать, почему компилятор "ругается". Как минимум, следует указывать сообщение об ошибке. А сэкономить можно много как. Например, объявить таблицу как const __flash, разместив ее тем самым не в SRAM (которое заполняется кодом инициализации из того же flash при запуске программы), а непосредственно во flash. Правда, доступ будет чуть сложнее, но зато таблица не займет оперативной памяти, что иногда важнее. А, кстати, а как на самом деле объявлялась таблица внутри? Надеюсь, что static? Иначе бедному компилятору пришлось бы ее инициализировать при каждом входе в функцию... Может, это и не понравилось? В общем, код в студию.

 

11 байт: то же самое. Бесцельно компилятор не выделяет лишние байты в конце. Причина обычно всегда есть. Но без фрагмента текста понять ее сложно. Мало кто на этом форуме умеет гадать на кофейной гуще :)

 

 

Компилятор глобальные переменные по умолчанию помещает в SRAM

Компилятор в любом случае помещает эти переменные в SRAM. Но в первом описанном случае помещает ещё и указатели на эти переменные. Ну и обращается к ним ... соответственно.

Компилятор не помещает их в SRAM, если явно указано, например,

 

char __regvar __noinit c @ 15;

 

При этом глобальная переменная c будет размещена не в SRAM, а в регистре, который мы обязаны залочить на уровне всего проекта опцией компилятора или IDE. Тут ответ был совершенно правильный.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...