невозможное ограничение в ассемблере при компиляции

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

Компилятор указывает на эту строку __asm__ __volatile__, я что-то пропустил?

#define OUTPORT PORTD
#define OUTBIT 3      // PD.3   
uint8_t rport ,rbits;
uint8_t *buf = message;
asm volatile(   "in     __tmp_reg__, __SREG__             \n\t" // 1  Save SREG for later
                "cli                                      \n\t" // 1  Clear interrupts
                "in     %[rport], %[port]                 \n\t" // 1  Move PORTB adress to rport

                : //Outputs
                [rport] "=&r" (rport)

                : //Inputs
                [port]    "I" (_SFR_IO_ADDR(OUTPORT))     // Adress to port register, 6-bit positive constant

                : //Clobber list (compiler must restore)

                "r0"                                       // This is __tmp_reg__
);
  1. Что разрушает эту сборку?
  2. Интересно, неправильный ли синтаксис asm? Я следую этому руководству

person Artec    schedule 11.06.2016    source источник
comment
Это очень здоровенный встроенный ассемблер. Убедитесь, что вам нужен ассемблер для этого, и если вы действительно думаете о том, чтобы поместить его в отдельный ассемблерный файл, а не встроенный. Сэкономит много головной боли.   -  person Jester    schedule 11.06.2016
comment
Это смехотворно длинный макрос. Почему бы не сделать это функцией и не поместить в файл сборки? Тогда ему даже не нужно быть изменчивым. Учитывая все его звонки и преднамеренные задержки, как это может повредить? И тогда вы получите адекватную диагностику. И если это будет вызываться более одного раза, ваш код станет меньше.   -  person DigitalRoss    schedule 11.06.2016
comment
Действительно ли _SFR_IO_ADDR(OUTPORT) имеет значение от 0 до 63?   -  person Michael Petch    schedule 12.06.2016
comment
Проголосовал за превращение этого в минимально воспроизводимый пример вместо смехотворно большого дампа кода. (Для будущих читателей: см. историю изменений оригинала, которая, вероятно, не оставила отключенными прерывания.)   -  person Peter Cordes    schedule 12.06.2016
comment
Я задаю свой предыдущий вопрос только потому, что мне интересно, не выходит ли он за пределы упомянутого диапазона, и, возможно, "M" будет более подходящим, чем "I".   -  person Michael Petch    schedule 12.06.2016
comment
@MichaelPetch Я пробовал даже M, но он жалуется на то же ограничение.   -  person Artec    schedule 12.06.2016
comment
@DigitalRoss, единственная причина Jester для встроенного - это требования к времени, а также наличие его в файле сборки еще больше усложняет его.   -  person Artec    schedule 12.06.2016
comment
Я предполагаю, что вы включили ‹avr/io.h›   -  person Michael Petch    schedule 12.06.2016
comment
Каково фактическое значение _SFR_IO_ADDR(OUTPORT)?   -  person David Wohlferd    schedule 12.06.2016
comment
Вы использовали rport как метку и как переменную. Попробуйте использовать другую метку, которая не конфликтует с именем переменной.   -  person uncleO    schedule 12.06.2016
comment
Здесь отлично работает с avr-gcc -mmcu=attiny2313 (версия 4.7.2).   -  person Jester    schedule 12.06.2016
comment
@Jester, я пытаюсь скомпилировать его с помощью avr-gcc -mmcu=atxmega128a4u (версия 4.9.2)   -  person Artec    schedule 12.06.2016


Ответы (1)


PORTD на ATxmega128A4U находится по адресу 0x0660, как указано в его техническое описание, стр. 62. Таким образом, порт недоступен для инструкции in. Вместо этого вы должны использовать lds с ограничением

[port]  "i" (_SFR_MEM_ADDR(OUTPORT))

Обратите внимание на строчную букву «i».

Дополнение. Я только что попытался скомпилировать следующее:

#include <avr/io.h>

void test(void)
{
    uint8_t rport;

    asm volatile(
        "in __tmp_reg__, __SREG__  \n\t"
        "cli                       \n\t"
        "lds %[rport], %[port]     \n\t"
        : [rport] "=&r" (rport)               // output
        : [port]  "i" (_SFR_MEM_ADDR(PORTD))  // input
        : "r0"                                // clobber
    );
}

Используя avr-gcc 4.9.2 с опциями -mmcu=atxmega128a4u -c, я получаю правильный сгенерированный код и никаких предупреждений, даже с -Wall -Wextra.

Ограничения "i" задокументированы как "непосредственный целочисленный операнд". , тогда как "I" означает «Константа больше −1, меньше 64”.

person Edgar Bonet    schedule 13.06.2016
comment
Ограничение нижнего регистра i неизвестно компилятору, и если я использую lds в разделе кода, он жалуется на операнд. - person Artec; 14.06.2016
comment
@Artec: avr-gcc 4.9.2 поддерживает ограничение i. Смотрите измененный ответ. - person Edgar Bonet; 14.06.2016
comment
Поскольку это обычная загрузка, вам не нужен встроенный ассемблер для загрузки из него. Просто volatile char* portd = (char*)_SFR_MEM_ADDR(PORTD). - person Peter Cordes; 14.06.2016
comment
@PeterCordes: В простом C я бы предпочел написать char* portd = PORTD;, что является более стандартной идиомой при программировании с помощью avr-libc (PORTD определяется как (*(volatile uint8_t *)(0x0660))). Эта идиома также работает с пространством ввода-вывода, поскольку компилятор оптимизирует доступ к in или out, когда это возможно. - person Edgar Bonet; 14.06.2016
comment
@EdgarBonet: а, понятно; Я не кодировал для AVR. Я видел подобные вопросы SO о MMIO на ARM. В любом случае, я думаю, вы имеете в виду uint8_t portd_value = PORTD;, поскольку макрос, по-видимому, включает в себя разыменование. - person Peter Cordes; 14.06.2016
comment
@EdgarBonet спасибо за разъяснения. следующая инструкция не работает, так как порт находится за пределами нижних 32 регистров ввода-вывода. Следующее вызывает ошибку операнда вне диапазона, даже SBS не поддерживается sbi %[port] , %[bit] \n\t // 1 Установить вывод HIGH - person Artec; 15.06.2016