Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Генератор индусского кода — HI-TECH ANSI C Compiler для PIC.  (Прочитано 32201 раз)
0 Пользователей и 1 Гость смотрят эту тему.
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« : 06-07-2011 19:01 » 

Пишу для PIC10F20x небольшой алгоритм - на Си это 10 строчек. Но пишу на ассемблере и основательно задолбался с goto и и мельканием movwf/movf/movlw. Решил попробовать написать на Си и посмотреть, как с этим справится компилятор. В MPLAB Studio много инструментов. Я выбрал HI-TECH ANSI C Compiler.

И так, исходный код на Си:

Код: (C)
///*
char test_func(void);

int main(void)
{
        char r;

        r = test_func();
}
//*/

char test_func(void)
{
        char v, z, d, s;

        v = 100;
        z = 0;

        if (v < z)
        {
                d = z - v;
                s = 0;
        }
        else
        {
                d = v - z;
                s = 1;
        }

        if (d < 2)
                return v;

        if (d > 25)
                d = 25;

        if (s == 0)
        {
                v = v + d;

                if (v > 100)
                        return 100;

                return v;
        }
        else
        {
                if (v < d)
                        return 0;

                return v - d;
        }
}

Так замучено писать заставил компилятор - он не понимает сравнений "d < 0", хотя char, вроде бы, тип знаковый. Возможно у него он по умолчанию бесзнаковый.



Посмотрим, что наиндусил компилятор...
(Ненужные директивы я убрал, а макросы развернул.)

Код: (ASM)
;_test.c: 14: char v, z, d, s;
;_test.c: 16: v = 100;
        movlw   (064h)
        movwf   (test_func@v)

Логично.
   
Код: (ASM)
l72:   
;_test.c: 17: z = 0;
        bcf status, c
        movlw 0
        btfsc status, с
        movlw 1
        movwf (test_func@z)

А это что?  Быть такого не может
1. Сбрасываем флаг переноса.
2. Загружаем в регистр W "0".
3. Проверяем флаг переноса и есть он чист, то пропускаем след. команду (переход на п.5)
4. Загружаем в регистр W "1".
5. Записываем в переменную.

А теперь сократим код (просто очистим переменную):

Код: (ASM)
        clrf test_func@z

Или так (загрузим ноль в переменную):

Код: (ASM)
        movlw 0
        movwf test_func@z

Аналогичный код:

Код: (ASM)
;_test.c: 22: s = 0;
        bcf status, c
        movlw 0
        btfsc   status,0
        movlw 1
        movwf (test_func@s)



Следующий перл:

Код: (ASM)
;_test.c: 19: if (v < z)
        movf    (test_func@z),w
        subwf (test_func@v),w
        btfsc status, c
        goto    u71
        goto    u70
u71:
        goto    l80
u70:

Зачем тут двойной переход (goto u71 / goto l80)?..  А черт его знает...



Еще "оптимизация":

Код: (ASM)
;_test.c: 21: d = z - v;
        movf    (test_func@z),w
        movwf btemp+3
        decf (test_func@v),w
        xorlw 0ffh
        addwf btemp+3,w
        movwf (test_func@d)

1. W = test_func@z
2. (btemp+3) = W
3. W = test_func@v - 1
4. W ^= 0xff
5. W += (btemp+3)
6. test_func@d = W

Замысловато...  С ума сойти...

Я бы сделал так:

Код: (ASM)
        movf test_func@v, w
        subwf test_func@z, w
        movwf test_func@d

Неправда ли, это короче и понятнее?

Аналогичный код:

Код: (ASM)
;_test.c: 26: d = v - z;
        movf (test_func@v),w
        movwf btemp+3
        decf (test_func@z),w
        xorlw 0ffh
        addwf btemp+3,w
        movwf (test_func@d)



Еще пример расточительства:

Код: (ASM)
;_test.c: 34: d = 25;
        movlw   (019h)
        movwf   (test_func@d)
        goto    l94
       
l12:   
       
l94:   
;_test.c: 36: if (s == 0)



Представляю, какой продукт будет на выходе с таким компилятором. У меня, замечу, оперативки всего 16 байт и программной памяти на 255 команд.
« Последнее редактирование: 06-07-2011 19:03 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 06-07-2011 20:01 » 

Ром, я когда вылизывал Си код для DSP - чтобы более менее сносно легло в asm, приходилось делать странные вещи)
вплоть до введения промежуточных переменных, и перемены мест слагаемых или строк, переворачивание циклов, изменение условий цикла и т.д.
в лоб было тоже довольно паршиво. после извратов - более менее, но конечного кода было килобайт на 5-10, мысли написать на асемблере который мало знаю(могу только оценить общий вид) - не возникало)
а компилятору можно и нужно помогать...  правда случаи разные бывают)))
« Последнее редактирование: 06-07-2011 20:02 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #2 : 06-07-2011 20:06 » 

Попробуйте то же самое с GCC и по возможности с IAR. К этим инструментам как-то больше доверия.
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #3 : 06-07-2011 21:05 » 

Dale, к сожалению IAR они сейчас не установлен, а GCC под PIC - не знал, что такое есть...

Собственно, я ж не зря в общение тему поместил Улыбаюсь
Или переместить в микроконтроллеры?

А алгоритм таки дался - он немудреный.

Код тут
Код еще не отлаженный. И оптимизировать есть чего.

Код: (ASM)
pwm_filter
    ; Simple outscoring filter.
    ;
    ; d = X(n) - X(n-1)
    ; d = abs(d) < 3 ? 0 : d;
    ; d = abs(d) > 25 ? sgn(d) * 25 : d;
    ; Y = X(n) + d
    ; Y = Y < 0 ? 0 : Y;
    ; Y = Y > 100 ? 100 : Y;
    ;
    ; Input and output values in range [0,100]

    ; Input: pwm_cnt, z_delta
    ; Output: pwm_cnt
    ; Used: tmp

    movf pwm_cnt, w
    subwf z_delta, w
    movwf z_delta ; z_delta = pwm_cnt - ppm_rate_z
    btfss STATUS, C
    comf z_delta, w
    movwf tmp ; tmp = abs(z_delta)

    movlw 3
    subwf tmp, w
    btfss STATUS, C
    goto pwm_filter_skip_all ; tmp < 3 -> skip all

    movlw 26
    subwf tmp, w
    btfss STATUS, C
    goto pwm_filter_skip_z_saturation ; tmp < 26 -> skip saturation
    LOAD tmp, 25
    btfsc z_delta, 7
    comf tmp, f
    COPY tmp, z_delta ; z_delta = 25 * sgn(z_delta)

pwm_filter_skip_z_saturation
    movf z_delta, w
    addwf pwm_cnt, f ; pwm_cnt = pwm_cnt + z_delta
    ; Here: pwm_cnt in range [-25,125] - must be saturated
    btfsc pwm_cnt, 7
    goto pwm_filter_test_Z_100 ; pwm_cnt > 0 -> follow to next test
    clrf pwm_cnt ; pwm_cnt = 0
    goto pwm_filter_final ; follow to finish

pwm_filter_test_Z_100
    movlw 101
    subwf pwm_cnt, w
    btfsc STATUS, C
    goto pwm_filter_skip_all ; pwm_cnt < 101 -> follow to finish
    LOAD pwm_cnt, 100 ; pwm_cnt = 100

pwm_filter_skip_all
    retlw 0

Чтобы в глазах меньше рябило использую макросы:

Код: (ASM)
COPY macro p_a, p_b
    movf p_a, w
    movwf p_b
    endm

LOAD macro p_var, p_const
    movlw p_const
    movwf p_var
    endm


Ochkarik, я ассемблером уже лет 10 или более того не занимался. Как раз потому, что очень долго и муторно.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #4 : 06-07-2011 21:35 » 

Dale, к сожалению IAR они сейчас не установлен, а GCC под PIC - не знал, что такое есть...

Похоже, не под все семейства PIC'ов есть GCC, так что мой совет может оказаться и бесполезным: http://ru.wikipedia.org/wiki/GCC#.D0.90.D1.80.D1.85.D0.B8.D1.82.D0.B5.D0.BA.D1.82.D1.83.D1.80.D1.8B

Я небольшой знаток PIC, так что про GCC для него знаю больше понаслышке, но люди пользуются.
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #5 : 07-07-2011 03:32 » 

Да, похоже только семейство PIC32 поддерживается.
Я тоже не знаток: 12 лет назад поработал с 16F84 и теперь вот с 10F200/202. Очень неудобный чип, зато очень дешевый и очень маленький.
Кстати, есть в этом чипе странность, отличающая его от остальных известных мне процессоров: флаг переноса у него инверсно работает. При вычитании 31h из 64h получается и 33h и STATUS.C устанавливается.

Добавлено через 3 часа, 7 минут и 5 секунд:
С флагом переноса оказалось  даже еще запутаннее:

1. (0x02 - 0x03) -> C = 0
2. (0x03 - 0x02) -> C = 1
2. (0xf0 + 0x20) -> C = 1
3. (0x10 + 0x20) -> C = 0

Т.е. он индицирует переполнение, либо инвертированный заем.

Отлаженный алгоритм - может кому сгодится.

(click to show)
Код: (ASM)
    ; Simple outscoring filter.
    ;
    ; d = X(n) - X(n-1)
    ; d = abs(d) < 3 ? 0 : d;
    ; d = abs(d) > 25 ? sgn(d) * 25 : d;
    ; Y = X(n) + d
    ; Y = Y < 0 ? 0 : Y;
    ; Y = Y > 100 ? 100 : Y;
    ;
    ; Input and output values in range [0,100]

    COPY ppm_rate_z, z_delta ; Temporary copy of Z (delay buffer).
    COPY pwm_cnt, ppm_rate_z ; Update Z.

    movf z_delta, w
    subwf pwm_cnt, w
    movwf z_delta ; z_delta = pwm_cnt - ppm_rate_z
    btfss STATUS, C
    comf z_delta, w
    movwf tmp ; tmp = abs(z_delta)

    movlw 3
    subwf tmp, w
    btfss STATUS, C
    goto pwm_filter_final ; tmp < 3 -> skip all

    movlw 26
    subwf tmp, w
    btfss STATUS, C
    goto pwm_filter_skip_z_sat ; tmp < 26 -> skip saturation
    LOAD tmp, 25
    btfsc z_delta, 7
    comf tmp, f
    COPY tmp, z_delta ; z_delta = 25 * sgn(z_delta)

pwm_filter_skip_z_sat
    movf z_delta, w
    addwf pwm_cnt, f ; pwm_cnt = pwm_cnt + z_delta
    ; Here: pwm_cnt in range [-25,125] - must be saturated
    btfss pwm_cnt, 7
    goto pwm_filter_test_Z_100 ; pwm_cnt > 0 -> follow to next test
    clrf pwm_cnt ; pwm_cnt = 0
    goto pwm_filter_final ; follow to finish

pwm_filter_test_Z_100
    movlw 101
    subwf pwm_cnt, w
    btfss STATUS, C
    goto pwm_filter_final ; pwm_cnt < 101 -> follow to finish
    LOAD pwm_cnt, 100 ; pwm_cnt = 100

pwm_filter_final
    movf pwm_cnt, w ; filter output

« Последнее редактирование: 07-07-2011 06:42 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 07-07-2011 06:41 » 

Очень неудобный чип, зато очень дешевый и очень маленький.

Точно. А поскольку труд программиста дорог, а при штучном тираже разница в цене рублей в 50 не играет никакой роли, то и овчина выделки не стоит. Пусть с ними китайские производственники мучаются в борьбе за несколько юаней.
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 07-07-2011 06:43 » 

Иногда дело не в юанях, а в граммах.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 07-07-2011 07:43 » 

RXL, вроде для него еще два есть...
MPLAB C18 and MPLAB C30 C Compilers
IAR C Compilers
или "оставь надежду всяк сюда входящий"?)
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #9 : 07-07-2011 11:13 » 

На момент эксперимента они у меня не были установлены - только минимум: MPASM и HI-TECH C.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
PredatorAlpha
Помогающий

us
Offline Offline

« Ответ #10 : 25-07-2011 09:24 » 

Когда-то пользовался HI-TECH под пики, вроде таких перлов не давал...
Поиграйся с настройками оптимизации (уровень оптимизации, размер/скорость), может пройдёт....
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #11 : 25-07-2011 09:48 » 

Спасибо. В настройках компиляции я пробовал галками пощелкать. Уже не помню какими - наверно всеми подходящими по смыслу к цели. Улучшения не заметил. Всего сделал 3-4 пробные компиляции. Возможно дело в серии Пика - у меня 12-я (pic10f200).

Не хочется больше с мелкими (10/12) Пиками связываться. Даже если придется, просто буду им только простые задачи решать, где ассемблера более чем достаточно.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
PredatorAlpha
Помогающий

us
Offline Offline

« Ответ #12 : 13-08-2011 11:51 » 

Код: (C)
        if (v < z)
        {
                d = z - v;
                s = 0;
        }
        else
        {
                d = v - z;
                s = 1;
        }

Код: (ASM)
l72:   
;_test.c: 17: z = 0;
        bcf status, c
        movlw 0
        btfsc status, с
        movlw 1
        movwf (test_func@z)

А это что?  Быть такого не может
1. Сбрасываем флаг переноса.
2. Загружаем в регистр W "0".
3. Проверяем флаг переноса и есть он чист, то пропускаем след. команду (переход на п.5)
4. Загружаем в регистр W "1".
5. Записываем в переменную.
Честно говоря, я логику оптимизатора немного понимаю
Изначально он выглядел где-то так:
1. Сбрасываем флаг переноса. (нужно для сравнения)
2. Загружаем в регистр W "0". (значение s в одной ветке)
- Производим сравнение (вычитание, результат - в перенос)
3. Проверяем флаг переноса и есть он чист, то пропускаем след. блок
- выполняем вычисление d в ветке, переход на п.5

Другая ветка
- вычисление d
4. Загружаем в регистр W "1".

5. Записываем в переменную s.

Лог оптимозации:

Сравнение выброшено, т.к. результат константа
Вычисление d выброшено, поскольку результат не используется (точнее, дальше сравнивается с константами, что компилятор безусловно выкинул, ведь результат - известен)

Получилось что-то типа:
Код: (ASM)
        // Условный переход по флагу на Ветка 2
        // Ветка 1
        movlw 0
        // Переход на модификацию

        // Ветка 2
        movlw 1

        // Модификация
        movwf (test_func@z)

Что потом было заменено на:
Код: (ASM)
        movlw 0
        btfsc status, с
        movlw 1

        // Модификация
        movwf (test_func@z)
Соптимизировали

в общем, то что получилось, можно назвать недооптимизация кода. Оптимизатор плохо отслеживает флаг переноса.
Конечный результат в идеале должен был выглядеть так:
Код: (ASM)
        movlw 0
        movwf (test_func@z)
Но, надо сказать, по сравнению с неоптимизированным в несколько раз сократил. Так что результат оптимизации можно оценить на хорошо.

ПС. хотя вру. Дальше s используется только для сравнения с нулём, то и этого кода быть не должно, в идеале компилятор должен оставить ТОЛЬКО нужную ветку кода, а потом и ёё сократить, просто вернув значение.
« Последнее редактирование: 13-08-2011 12:11 от PredatorAlpha » Записан
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #13 : 13-08-2011 17:08 » 

а если так?)
Код: (C++)
       d = z - v;
       if (v < z)
        {
                s = 0;
        }
        else
        {
                d = -d;
                s = 1;
        }
или так
Код: (C++)
       s = 0;
       d = z - v;
       if (v >= z)
       {
                d = -d;
                s = 1;
        }
или
Код: (C++)
       s = 1;
       d = v - z;
       if (v < z)
       {
                d = -d;
                s = 0;
        }
мне помогало иногда.
PS если нигде не напутал)
« Последнее редактирование: 13-08-2011 17:16 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #14 : 13-08-2011 18:52 » new

Можно еще попробовать вариант без явного использования условных операторов:
Код: (C)
        d = abs(z - v);
        s = !(v < z);
Возможно, он оптимизируется лучше.

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

Цитата из мануала по GCC:

Цитата
Depending on your system, the char data type is defined as having the same range as either the signed char or the unsigned char data type (they are three distinct types, however). By convention, you should use the char data type specifically for storing ASCII characters (such as ‘m’), including escape sequences (such as ‘\n’).

Я вообще предпочитаю использовать тип char только для хранения литер, чтобы не попадать в такие ловушки.
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Ochkarik
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #15 : 13-08-2011 18:57 » 

Dale, в смысле signed/unsigned всегда указывать?
да, лучше так...
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Dale
Блюзмен
Модератор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #16 : 13-08-2011 19:35 » 

Dale, в смысле signed/unsigned всегда указывать?

Нет, мне больше нравится использовать int8_t или uint8_t там, где нужно работать именно с байтами. Лучше показывает намерения программиста. Тип char использую только в случаях, когда в нем действительно должна храниться литера. Компилятору все равно, а людям, читающим программу, так понятнее.
Записан

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

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #17 : 13-08-2011 20:01 » 

Насчет stdint.h - поддерживаю. Четко знаешь, что у тебя за размер.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines