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

Подсчитать четность битов в байте для AVR

On 12/9/2022 at 10:31 AM, vvppvv said:

Э не-е-ет, так не пойдёт. Иначе придётся распрощаться с оптимизацией IAR! Много лет назад я эти грабли уже проходил, "Странно, почему, когда включаешь оптимизацию, то программа перестаёт нормально работать?" Компилятор пасёт состояния всех регистров и переменных. Если он когда-то в R17 положил ноль, и больше его не трогал, то когда в следующий раз ему понадобится ноль в каком-нибудь порту, то он его с R17 и дёрнет. А там уже кака. 

Почитайте в руководстве компилятора соглашения о вызовах функций.

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


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

1 hour ago, quark said:

После инструкции EOR R17,R16 имеем результат побитного сложения полубайтов в двух экземплярах - в старшем и младшем полубайтах R17 один и тот же. [ ....... ] Суммируем побитно 7-й и 1-й биты инструкцией SUBI R17,0xFE (на самом деле это ADDI R17,0x02). Имеем конечный результат в 7-м бите R17.

Спасибо! ))

1 hour ago, dimka76 said:

Почитайте в руководстве компилятора соглашения о вызовах функций.

Ну, я как бы в курсе. Потому и написАл про "костыль", из-за которого функция якобы возвращает 2 байта. Из которых нужен один, а второй просто указывает компилятору, что где-то внутри функции он испорчен. Если есть цивилизованый способ как-то "обволятилить" R17, то подскажите? ))

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


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

26 минут назад, vvppvv сказал:

Если есть цивилизованый способ как-то "обволятилить" R17, то подскажите?

ИАР всегда отличался неплохой документацией и эта документация всегда шла в комплекте. Там искали? На сайте IAR выложена документация от 2017 года, в которой английским по белому сказано: "никак".

image.thumb.png.6ca7dd35acb986399e1056b9752a32d2.png

Если с тех пор ничего не поменялось - переходите на GCC, там компилятору можно объяснить все и он даже сам может выбрать наиболее подходящие регистры:

    asm volatile (
        "LD %[tmp], %a[ptr]+ \n\t"
        "STS %B[OCR], %[tmp] \n\t"
        "LD %[tmp], %a[ptr]+ \n\t"
        "STS %A[OCR], %[tmp] \n\t"

        "LD %[tmp], %a[ptr]+ \n\t"
        "OUT %[port_b], %[tmp] \n\t"

        "LD %[tmp], %a[ptr]+ \n\t"
        "OUT %[port_c], %[tmp] \n\t"

        : [ptr]"+e"(pTmp), [tmp]"=r"(Tmp)
        : [OCR] "M" (_SFR_MEM_ADDR(OCR1A))
        , [port_b] "I" (_SFR_IO_ADDR(PORTB))
        , [port_c] "I" (_SFR_IO_ADDR(PORTC))
        :
    );

.

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


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

Понятно. Раз никак, значит, никак. ))

Я попытался переписать асемблер на Си. Все по алгоритму выше. точь в точь. Вместо R17 добавить локальный "ctemp". Но IAR почему-то зачудил. При оптимизации вобще все выкинул, кроме andi 0x7F. Если оптимизацию отключить совсем, получается почти хорошо:

   \                     cSetParity:
    380            {
    381            char ctemp;
    382            cin &= 0x7F;    // сбросить 7-ой бит чётности в 0
   \   00000000   770F               ANDI    R16, 0x7F
    383            ctemp = cin;
   \   00000002   2F10               MOV     R17, R16
    384            __swap_nibbles(ctemp);
   \   00000004   2F21               MOV     R18, R17
   \   00000006   9522               SWAP    R18
    385            ctemp ^= cin;
   \   00000008   2710               EOR     R17, R16
    386            ctemp &= 0xC3;
   \   0000000A   7C13               ANDI    R17, 0xC3
    387            ctemp += 0x41;
   \   0000000C   5B1F               SUBI    R17, 191
    388            ctemp |= 0x7C;
   \   0000000E   671C               ORI     R17, 0x7C
    389            ctemp += 0x02;
   \   00000010   5F1E               SUBI    R17, 254
    390            ctemp &= 0x80;
   \   00000012   7810               ANDI    R17, 0x80
    391            cin |= ctemp;
   \   00000014   2B01               OR      R16, R17
    392            return cin;
   \   00000016   9508               RET
    393            }

 Почему "почти"?  См. свап ниблов. Откуда-то вылез R18, который после нигде не фигурирует. Играясь "оптимизЭйшын левелами", я добился нужного результата в листинге, но меня это не устраивает. Когда результат зависит от настроения конкретного релиза компилятора.

Так что останется инлайн ассемблер. Вопрос, к чему все мои извраты, ради каких-то 5 тактов? Просто это используется с большой скоростью, для передачи данных. Для массива 1 мегабайт, 5 тактов на 1 байт дадут общий выигрыш 5 миллионов тактов.

Переходить на GCC... не уверен, что оно того стОит. Уж больно много лет я сижу в IAR. Да и оптимизирует он код по размеру просто чудовищно эффективно. А у меня часто быват проектики для самых мелких камешков, не влезло  - следующий чип, а его или нет, или рублей на 50 дороже. 1000 изделий сразу тянут плюсом 50 тыщ рублей, на ровном месте, и пошло, поехало... ((

 

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


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

34 минуты назад, vvppvv сказал:

Почему "почти"?  См. свап ниблов. Откуда-то вылез R18, который после нигде не фигурирует.

Может быть стоило написать ctemp = __swap_nibbles(ctemp); ?

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


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

On 12/9/2022 at 4:22 PM, Сергей Борщ said:

Может быть стоило написать ctemp = __swap_nibbles(ctemp); ?

:-))  Спасибо!

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

    377          char cSetParity (char cin)
   \                     cSetParity:
    378            {
    379            char ctemp;
    380            cin &= 0x7F;    // сбросить 7-ой бит чётности в 0
   \   00000000   770F               ANDI    R16, 0x7F
    381            ctemp = cin;
   \   00000002   2F10               MOV     R17, R16
    382            ctemp = __swap_nibbles(ctemp);
   \   00000004   9512               SWAP    R17
    383            ctemp ^= cin;
   \   00000006   2710               EOR     R17, R16
    384            ctemp &= 0xC3;
   \   00000008   7C13               ANDI    R17, 0xC3
    385            ctemp += 0x41;
   \   0000000A   5B1F               SUBI    R17, 191
    386            ctemp |= 0x7C;
   \   0000000C   671C               ORI     R17, 0x7C
    387            ctemp += 0x02;
   \   0000000E   5F1E               SUBI    R17, 254
    388            ctemp &= 0x80;
   \   00000010   7810               ANDI    R17, 0x80
    389            cin |= ctemp;
   \   00000012   2B01               OR      R16, R17
    390            return cin;
   \   00000014   9508               RET
    391            }

Спасибо большое всем за алгоритмы и подсказки!

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


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

В 12.12.2022 в 08:48, vvppvv сказал:

Спасибо большое всем за алгоритмы и подсказки!

Так выглядит реализация доработанного алгоритма программы:

ANDI R16,0x7F
MOV  R17,R16
ADD  R17,R16
EOR  R17,R16
ANDI R17,0xAA
SUBI R17,0x9A
ANDI R17,0x88
SUBI R17,0x88
ANDI R17,0x80
OR   R16,R17

По параметрам, он полностью аналогичен предыдущему варианту.
Но из него исключена инструкция SWAP, которая имеется не во всех системах команд.
В данном алгоритме используются только арифметические и логические команды.

P.S. Проверьте.

--------------------

P.P.S.
Если удалить из программы первую и последнюю строку (даже две последних),
то алгоритм будет работать и для полного байта - для всех 8-ми бит.
Конечный результат - бит четности - будет в 7-м бите R17.

Изменено пользователем quark
добавление

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


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

Добрый день!

Проверил. Не знаю как, но тоже работает. ))

Компилятор, увидев сложение самое с собой ("ADD R17,R16"), вставил "LSL  R16" 🙂

Инструкция SWAP в ассемблерах мне встречалась часто, но вот чтобы её использовать в Си для AVR, приходится подключать и применять intrinsic-функции, что привязано к конкретному железу и компилятору, т.е. неуниверсально. Потому без SWAP, конечно же, будет корректнее, на чистом Си:

char cSetParity (char cin)
  {
  char ctemp;
  ctemp = cin;
  ctemp += cin;
  ctemp ^= cin;
  ctemp &= 0xAA;
  ctemp -= 0x9A;
  ctemp &= 0x88;
  ctemp -= 0x88;
  ctemp &= 0x80;
  cin |= ctemp;
  return cin;
  }

Как говорится, "Ну вот, теперь можно в рамочку, и на стену". )) 

Спасибо!

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


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

15 минут назад, vvppvv сказал:

Инструкция SWAP в ассемблерах мне встречалась часто, но вот чтобы её использовать в Си для AVR, приходится подключать и применять intrinsic-функции,

У меня конструкция 

byte = (byte >> 4)|(byte << 4);

обычно оптимизировалась в swap. С включенной оптимизацией, разумеется (без оптимизации не компилирую).

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


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

21 минуту назад, vvppvv сказал:

Компилятор, увидев сложение самое с собой ("ADD R17,R16"), вставил "LSL  R16" 🙂

Наверное, все-таки, "LSL R17". Но компилятору, конечно, виднее... 🙂

21 минуту назад, vvppvv сказал:

Как говорится, "Ну вот, теперь можно в рамочку, и на стену". )) 

Для варианта на С, чтобы "на стену в рамочке", надо бы заменить: - 0x9A на +0x66, а - 0x88 на +0x78, чтобы совсем было по "фэншую", как в исходном алгоритме.

Хотя, разницы нет, конечно.

 

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

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


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

3 minutes ago, quark said:

Наверное, все-таки, "LSL R17". Но компилятору, конечно, виднее...

Конкретно в том месте, компилятор просто решил использовать R16 как ctemp и R17 как cin. ))

7 minutes ago, quark said:

Для варианта на С, чтобы "на стену в рамочке", надо бы заменить: - 0x9A на +0x66, а - 0x88 на +0x78, чтобы совсем было по "фэншую", как в исходном алгоритме.

Ну, для понятия алгоритма (для, так сказать, визуализации) - да, корректней было бы написать сложение. Переписал. Это у же в кодах, по факту, будет SUBI. ))

    398          char cSetParity (char cin)
   \                     cSetParity:
    399            {
    400            char ctemp;
    401            ctemp = cin;
   \   00000000   2F10               MOV     R17, R16
    402            ctemp += cin;
   \   00000002   0F10               ADD     R17, R16
    403            ctemp ^= cin;
   \   00000004   2710               EOR     R17, R16
    404            ctemp &= 0xAA;
   \   00000006   7A1A               ANDI    R17, 0xAA
    405            ctemp += 0x66;
   \   00000008   591A               SUBI    R17, 154
    406            ctemp &= 0x88;
   \   0000000A   7818               ANDI    R17, 0x88
    407            ctemp += 0x78;
   \   0000000C   5818               SUBI    R17, 136
    408            ctemp &= 0x80;
   \   0000000E   7810               ANDI    R17, 0x80
    409            cin |= ctemp;
   \   00000010   2B01               OR      R16, R17
    410            return cin;
   \   00000012   9508               RET
    411            }

 

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


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

21 минуту назад, vvppvv сказал:

Ну, для понятия алгоритма (для, так сказать, визуализации) - да, корректней было бы написать сложение. Переписал. Это у же в кодах, по факту, будет SUBI. ))

Первая строчка потерялась в программе:  cin &= 0x7F;     (ANDI  R16,0x7F)

 

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

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


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

29 minutes ago, quark said:

Первая строчка потерялась в программе:  cin &= 0x7F;     (ANDI  R16,0x7F)

Да, это была просто перестраховка, в начале опытов. На самом деле входные символы используют только первую половину ASCII-кодов, так что седьмой (старший) бит теоретически на входе должен быть всегда 0. А на выходе должен дополнять байт до чётного количества битов. Алгоритм уже опробован на реальном железе (старые системы ЧПУ типа FANUC-6, FANUC-0mc) и показал полную работоспособность! ))  Поэтому я эту команду обнуления убрал, как ненужную.

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

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


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

15 минут назад, vvppvv сказал:

Да, это была просто перестраховка, в начале опытов. На самом деле входные символы используют только первую половину ASCII-кодов, так что седьмой (старший) бит теоретически на входе должен быть всегда 0. А на выходе должен дополнять байт до чётного количества битов. Алгоритм уже опробован на реальном железе (старые системы ЧПУ типа FANUC-6, FANUC-0mc) и показал полную работоспособность! ))  Поэтому я эту команду обнуления убрал, как ненужную.

Для варианта "в рамочке на стене", все-таки, приведу полный вариант:

char cSetParity (char cin)
  {
  char ctemp;
  cin &= 0x7F;
  ctemp = cin;
  ctemp += cin;
  ctemp ^= cin;
  ctemp &= 0xAA;
  ctemp += 0x66;
  ctemp &= 0x88;
  ctemp += 0x78;
  ctemp &= 0x80;
  cin |= ctemp;
  return cin;
  }

Теперь совсем "конфетка" получилась - кроссплатформенная. 🙂

 

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


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

Тогда уж, для универсальности и кросс-платформенности, придётся какие-нибудь ifdef'ы дописать, сколько бит в байте (7 или 8), и куда совать бит четности, на позицию 7-го бита, или в отдельный байт. Чтоб выполнялось:

On 1/4/2023 at 10:12 PM, quark said:

P.P.S.
Если удалить из программы первую и последнюю строку (даже две последних),
то алгоритм будет работать и для полного байта - для всех 8-ми бит.
Конечный результат - бит четности - будет в 7-м бите R17.

Тогда конфетка точно будет. 🙂 

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

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


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

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

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

Гость
Ответить в этой теме...

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

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

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

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

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

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