|
Как портабельно получить DDRx через PORTx?, Или игры с SFR-ами на IAR |
|
|
|
Mar 2 2006, 10:12
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
При портировании AVR-USB драйвера с GCC на IAR вылезла небольшая проблема с точки зрения эффективности кода. Автор оригинала использует следующие конструкции, которые нормально проходят в GCC: Код #define USB_CFG_IOPORT PORTD ... #define USBIN (*(&USB_CFG_IOPORT - 2)) /* input port for USB bits */ #define USBDDR (*(&USB_CFG_IOPORT - 1)) /* data direction for USB bits */ Под IAR это все транслируется, но превращается вот в такой вот кошмар: Код // USBDDR |= 0x10; // USB_CFG_IOPORT |= 0x10; IN R16, 0x11 ORI R16, 0x10 OUT 0x11, R16
SBI 0x12, 0x04 То есть, непосредственное обращение к регистру использует SBI, а обращение к указанной конструкции - последовательность команд, не считая определение константным выражением. Перепробовал несколько идей - не помогло. Изменить определение самого порта с PORTx на просто x с последующей конкатенацией префиксов с именем порта не подходит, так как автор ориентируется в первую очередь на GCC и едва ли согласится радикально менять код, конфигурируемый пользователем. Для себя-то я могу выйти из положения. Но работая совместно с Кристианом над официальным портом для IAR, хотелось предложить что-то более-менее портабельное без ухода от выбранного им способа определения портов. Нет у кого идей? Подходит вариант с условной компиляцией (то есть, для именно IAR можно использовать тут __io и тому подобные специфические конструкции). Важно лишь, чтобы раскрывались они в конечном итоге в SBI, а не чтение-модификацию-запись покомандно.
|
|
|
|
|
 |
Ответов
(1 - 9)
|
Mar 3 2006, 11:30
|
Местный
  
Группа: Свой
Сообщений: 298
Регистрация: 29-08-05
Пользователь №: 8 064

|
Цитата(osnwt @ Mar 2 2006, 14:12)  Нет у кого идей? Подходит вариант с условной компиляцией (то есть, для именно IAR можно использовать тут __io и тому подобные специфические конструкции). Важно лишь, чтобы раскрывались они в конечном итоге в SBI, а не чтение-модификацию-запись покомандно. Идея впринципе одна, ASM. Причина такого кода заключена в том что сам порт обьявлен как __io а это означает что порт обьявляется как volatileСделано это для того чтобы при оптимизации его (порт) не выкинули. А поскольку он может измениться неведомо компилятору то он его перед изменением и вычитывает. Вот такая вот причина.... А для того чтобы было SBI CBI используй PORTD_Bit0 = 1; // SBI PORTD_Bit0 = 0; // CBI и вкл Enable Bit Difinitions в Genera/ System
|
|
|
|
|
Mar 3 2006, 12:02
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Цитата(&-rey @ Mar 3 2006, 13:30)  Идея впринципе одна, ASM. Идеи не понял - при чем тут ASM?  Цитата Причина такого кода заключена в том что сам порт обьявлен как __io а это означает что порт обьявляется как volatile Осмелюсь напомнить, что и PORTD объявлен точно также. Цитата А поскольку он может измениться неведомо компилятору то он его перед изменением и вычитывает. Рассуждения понятны, но это не из той оперы. Самое оптимальное для изменения порта - как раз использовать sbi, выполняющую все аппаратно. Я же не использую больше прочитанное значение. Цитата А для того чтобы было SBI CBI используй PORTD_Bit0 = 1; // SBI PORTD_Bit0 = 0; // CBI и вкл Enable Bit Difinitions в Genera/ System За подсказку спасибо, но я про эти особенности IAR знаю. Однако выше я писал, что "для себя-то я могу выйти из положения." и т.п. Вопрос в том, есть ли более-менее портабельный вариант. Предложенная конструкция с PORT_Bit, к сожалению, к таковым не относится. Тем более, что для заданного вопроса эта конструкция неприменима в принципе. Я ведь спрашивал не про замену (PORTD |= 0x10) - это как раз транслируется оптимально в sbi без всяких PORTD_Bit4 = 1 (IAR достаточно разумен для такой оптимизации). А про замену для (*(&PORTD-1) |= 0x10) как эквивалента (DDRD |= 0x10). Это к вопросу об исходной постановке задачи  . (Расшифровка: PORTD для IAR - это переменная с модификатором __io, находящаяся по фиксированному адресу (порта), являющемуся тем самым адресом этой переменной. Операция получения адреса &PORTD дает нам этот самый адрес. Вычитая из него единицу, получаем адрес DDRD. А далее используем *(адрес DDRD) для работы, собственно, с самим значением. Все формально правильно, но IAR не воспринимает полученное значение как константное выражение адреса __io порта. Добавление всяких модификаторов типа const или __io с разными приведениями типа мне не помогло. Возможно, я не всё попробовал. В этом и состоял исходный вопрос, а не в том, как избавиться от чтения-модификации-записи - где IAR считает нужным, он это делает и сам. А вот как ему помочь в данном случае?). PS. Почему-то, когда задаются банальные вопросы типа "как объявить переменную по фиксированному адресу", сразу же следует море правильных и не очень ответов. Стоит спросить что-то менее тривиальное - и полная тишина. Почти полная, в данном случае, так как одна попытка ответить все же была (хоть и не на тот вопрос)  . PPS. Не считая мелких нюансов, связанных с оптимизацией C-кода под gcc-avr, и не столь эффективную компиляцию под IAR, официальный порт avr-usb для IAR, можно сказать, уже готов. Еще пара мелких штришков по документации, и он может появиться на официальной странице проекта.
Сообщение отредактировал osnwt - Mar 3 2006, 12:07
|
|
|
|
|
Mar 3 2006, 13:08
|
Местный
  
Группа: Свой
Сообщений: 298
Регистрация: 29-08-05
Пользователь №: 8 064

|
Цитата(osnwt @ Mar 3 2006, 16:02)  Идеи не понял - при чем тут ASM?  Возможно я не совсем понял вопроса.  Попробую изложить как я понял ... конструкция типа Код (PORTD |= 0x10) - это транслируется оптимально А Код (*(&PORTD-1) |= 0x10) как эквивалента Код (DDRD |= 0x10) приводит к выше упомянутому коду.... Теперь если я врубился попробую снова  когда мы пишем PORTD то компилятор точно знает что от него требуется и генерит то чего мы ждем от него. например если написать Код (PORTD |= 0x30) то это уже разложится на Код 000096 B302 IN R16,PORTD 000098 6300 ORI R16,0x30 00009A BB02 OUT PORTD,R16 когда (&PORTD-1) то он знает лишь то что PORTD у нас __io (volatile) и более ничего, поэтому поступает с ним соответственно. собственно поэтому я и предложил решать это ASM вставкой, хотя я тоже не в восторге от такого подхода  хотя если мотреть на то что он знает что мы обращаемся к DDRD Код *(&PORTD - 1) |= 0x10; 00009C B301 IN R16,DDRD 00009E 6100 ORI R16,0x10 0000A0 BB01 OUT DDRD,R16 согласен что компилятор мог бы и подумать  (((
|
|
|
|
|
Mar 3 2006, 13:36
|

Частый гость
 
Группа: Свой
Сообщений: 175
Регистрация: 26-01-06
Из: Sevastopol
Пользователь №: 13 664

|
Действительно, вопрос понят не совсем правильно. Цитата когда мы пишем PORTD то компилятор точно знает что от него требуется и генерит то чего мы ждем от него. например если написать Код (PORTD |= 0x30) то это уже разложится на Код 000096 B302 IN R16,PORTD 000098 6300 ORI R16,0x30 00009A BB02 OUT PORTD,R16 Когда мы пишем (PORTD |= 0x30), то компилятору в любом случае не остается ничего, кроме как представить это тремя командами (можно двумя, но это уже нюансы). В данном случае везде используется вариант с установкой строго одного бита: (PORTD |= 0x10). И если PORTD - это стандартное определение порта, то будет sbi. Если вместо PORTD использовано стандартное определение DDRD, то тоже будет sbi. Но вот если использовано (применимое только к AVR) тайное знание того, что адрес DDRD = адрес PORTD-1, то как ни напиши - компилятор упорно не хочет использовать sbi, что приводит к замене одной команды тремя. Учитывая то, что это повторяется в коде несколько раз, мы заметно проигрываем в размере. Вопрос состоял в том, как корректно получить экивалент стандартного определения порта DDRD, зная лишь базовый порт PORTD. Притом, так, чтобы компилятор понимал, что этот эквивалент - это точно такое же определение, как и его штатное, и работал с ним соответственно. Естественно, что всё это - константные выражения, адрес порта не меняется в run-time. От того и обидно, что мне пока не удалось этого сделать Цитата когда (&PORTD-1) то он знает лишь то что PORTD у нас __io (volatile) и более ничего, поэтому поступает с ним соответственно. А, вот что имелось в виду... Понятно. Фокус тут в том, можно ли каким-либо путем убедить компилятор, что (&PORTD-1), хоть это и адрес volatile переменной, но сам он является const-антой. А дальше - что это - константный адрес __io переменной, которая сама по себе volatile, но такие ведь все порты. Возможно, какими-то приведениями типов и можно это сделать, но я пока не преуспел. Цитата собственно поэтому я и предложил решать это ASM вставкой, хотя я тоже не в восторге от такого подхода  Автор avr-usb - тем более. Он не склонен делать слишком много явно бросающихся в глаза модификаций текста для коммерческого компилятора, так как проект ориентирован (и сильно) на gcc-avr, и сильно под него оптимизирован без ассемблерных вставок. Заменить, скажем, __io(x) на пустую конструкцию для компиляции под gcc он согласится, а вот делать множество условных #if - нет, если в принципе проект собирается (а он собирается и работает). Но вопросы оптимизации формы представления - это уже вторично. Мне бы хоть под IAR понять, как выкрутиться из положения. PS. После некоторого раздумья становится понятно, в чем может быть проблема (но не как ее решать). Ведь макро типа DDRD определяются через структуру, размещаемую по некоему фиксированному адресу в сегменте __io (образно говоря). В целом может быть так, что знание этого адреса во время трансляции штатных структур компилятору недоступно, а закладывается в информацию для линкера, который лишь на стадии линковки может прописать туда реальное значение, а также его же -1 - в те места, где на него ссылаются. Если так, то сделать более корректно и унифицированно не получится в принципе  Так что желающим оптимизировать и это придется заняться hand job (ручной работой по коверканью исходников, а не тем, что часто под этим подразумевают  )
Сообщение отредактировал osnwt - Mar 3 2006, 13:42
|
|
|
|
|
May 23 2006, 11:59
|
Местный
  
Группа: Участник
Сообщений: 416
Регистрация: 18-04-06
Из: Челябинск
Пользователь №: 16 219

|
Цитата(osnwt @ Mar 2 2006, 13:12)  Нет у кого идей? Подходит вариант с условной компиляцией (то есть, для именно IAR можно использовать тут __io и тому подобные специфические конструкции). Важно лишь, чтобы раскрывались они в конечном итоге в SBI, а не чтение-модификацию-запись покомандно. Если gcc не умеет работать с портами, то я бы ввел условную трансляцию. Тем более, что наверняка есть еще отличия, не только при обращении к портам. Например, процедуры обработки прерываний по-разному описываются.
|
|
|
|
Guest_Гость_*
|
May 23 2006, 12:13
|
Guests

|
Цитата(_Bill @ May 23 2006, 14:59)  Если gcc не умеет работать с портами, то я бы ввел условную трансляцию. Тем более, что наверняка есть еще отличия, не только при обращении к портам. Например, процедуры обработки прерываний по-разному описываются. Вопрос был только по этому месту - остальные давно решены. Условная трансляция тут не поможет, так как вопрос в том, как полупортабельно получить адрес соседнего порта при том. что адреса SFR-ов - это вообще в принципе непортабельная штука с точки зрения C. Используемая конструкция работает в обоих компиляторах. Проблема только в том, что у IAR получается избыточность в 2 байта кода. За малостью названного автор не стал что-то придумывать, так как основной прицел был GCC, а IAR - постольку-поскольку.
|
|
|
|
|
May 23 2006, 13:04
|
Местный
  
Группа: Участник
Сообщений: 416
Регистрация: 18-04-06
Из: Челябинск
Пользователь №: 16 219

|
Цитата(Гость @ May 23 2006, 15:13)  Вопрос был только по этому месту - остальные давно решены. Условная трансляция тут не поможет, так как вопрос в том, как полупортабельно получить адрес соседнего порта при том. что адреса SFR-ов - это вообще в принципе непортабельная штука с точки зрения C.
Используемая конструкция работает в обоих компиляторах. Проблема только в том, что у IAR получается избыточность в 2 байта кода. За малостью названного автор не стал что-то придумывать, так как основной прицел был GCC, а IAR - постольку-поскольку. Что значит "соседний порт"? Насколько необходима здесь эффективность? И вообще, переносимость и эффективность часто являются противоречивыми требованиями, и выбирать приходится что-то одно.
|
|
|
|
|
Oct 24 2006, 08:29
|
Местный
  
Группа: Свой
Сообщений: 278
Регистрация: 18-01-05
Из: Санкт-Петербург
Пользователь №: 2 031

|
Мне тоже пришлось столкнутся с похожей проблемой и вот что я выяснил. IAR не поддерживает указатели в __io области и по идее должен ругаться на такие финты... Однако этого не происходит. В документации также сказано что Цитата An assignment to a bitfield always generates a write access, in some cases also a read access. If only one bit is updated—set or cleared—the sci/cbi instructions are executed. Но это относится только к Цитата __io declared variables located in 0x–0x1F указатели не являются located variable с точки зрения компилятора поэтому до уровня sci/cbi они и не оптимизируются! Т.к. для операции разыменования указателя спецификаторы памяти в том числе и __io неприменимы, то данная задача в случае с IAR решения не имеет.
|
|
|
|
|
Oct 25 2006, 08:14
|
Местный
  
Группа: Свой
Сообщений: 278
Регистрация: 18-01-05
Из: Санкт-Петербург
Пользователь №: 2 031

|
Нарыл ещё кое-что интересное. Вам это наверное не очень поможет, потому что надо включать EC++, но может кому интересно будет. Цитата It is allowed to have a reference to a memory type as a template parameter, even if pointers to that memory type are not allowed. попробовал, с шаблонами действительно работает! т.е. в листинге появились команды sbi/cli вместо чтение-модификация-запись!
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|