Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Подскажите пожалуйста, про многозадачность.
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Страницы: 1, 2
bodja74
2vod

Смотрел,хорошая оська,незнаю как насчет фунциональности,так как глубоко не копал,
но написано стильно и понравились кое какие нестандартные приемы,в частности реализация косвенного перехода на подпрограмму (задачу) с маневрами со стеком и т.д.
Оказывается не только я такой "экстремал-извращенец",
хотя конечно вот это необязательно было делать
Код
____INIT_THREAD_CONTEXT:
    std    Y+CONST_THREAD_CONTEXT_SIZE+3,r24;save SPL
    std    Y+CONST_THREAD_CONTEXT_SIZE+2,r25;save SPH
    std    Y+CONST_THREAD_CONTEXT_SIZE+1,REG_OS_TEMP;save SREG
    std    Z+CONST_THREAD_AMOUNT,Yh    
    st    Z+,Yl    
    adiw    Y,CONST_THREAD_MEM_SIZE
    RET
;------------------------------------------------

Для этого есть такая рееедко применяемая ,но всетаки существующая команда косвенного перехода на подпрограмму по Z icall . smile.gif

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

1 Заместь векторов в таблице указываем вызов планировщика
Например заместь
rjmp PROG
rjmp INT0
rjmp INT1
.....

Пишем
rcall PLAN_START
rcall PLAN
rcall PLAN
.....

Я думаю догадались для чего заместь rjmp стоит rcall smile.gifsmile.gifsmile.gif Чтобы планировчик по стеку смог определить какой адресс вектора был у прерывания .

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

3 Естественно в PLAN_START обнуляем память до RAMEND-1 ,последняя ячейка у нас для выбора программы,ну и т.д.

Вот такие извращения. smile.gif

Что нам это дает?
Можно просто тупо соединять несколько программ,с сохранением абсолютно ВСЕХ прерываний для каждой программы без каких либо переделок,все что нужно если хотим программно перепрыгивать из одной программы в другую - дописать в нужном месте номер проги в последнюю ячейку и call PLAN_START smile.gifsmile.gifsmile.gif
yod
to bodja74
Цитата(bodja74 @ Oct 27 2006, 05:05) *
хотя конечно вот это необязательно было делать
Код
____INIT_THREAD_CONTEXT:

Этим вызовом я инициализирую
1. в стек задчи сохраняю адрес возврата (в д.с. стартовый адрес)
2. инициализирую соотв. ячейку в таблице указателей задач текущим указателем стека задачи
после всей инициализации молча прыгаю в планировщик. там пусть сам выбирает smile.gif
[offtopic]
Цитата(bodja74 @ Oct 27 2006, 05:05) *
Для этого есть такая рееедко применяемая ,но всетаки существующая команда косвенного перехода на подпрограмму по Z icall . smile.gif

Я пока "не догнал", как там "матеро" можно его использовать, но будем думать. Там еще проблема.
Я регистр Z оставил для пользования прерываниям - такая специфика - надо каждые __256 тактов__ _гарантировано_ обновить ШИМ-регистр таймера. А все значения в таблице прописаны во флэш.
таблица 1кб smile.gif
Цитата(bodja74 @ Oct 27 2006, 05:05) *
Скажу чесно,оси никогда не цеплял,так как считаю что асм потеряет свои преимущества перед другими языками при ее применении.

ИМХО напрасно, если писать что-то тяжелее "эха" на уарте.
Цитата(bodja74 @ Oct 27 2006, 05:05) *
Но своебразную многозадачность реализовывал.Сами знаете ,частенько на асме проги небольшие выходят .Вот и захотельсь мне зделать многофункциональный девайс ,тоесть впихнуть несколько программ (которые были уже написаны) в один контроллер.

Ну то что Вы копали, спасибо кстати, это и есть "реализация своеобразной многозадачности".
про проги небольшие молчу - свою "ОС" пользую когда прога занимает от 1кб.
[/offtopic]
Цитата(bodja74 @ Oct 27 2006, 05:05) *
Я не навязываю свой способ ,но я думаю будет интересен.Раскажу вкратце.
...

Идею понял. Поздравляю, редкостное извращение smile.gif
Я придерживаюсь тех позиций что прерывание должно отработать максимально быстро.
И прерывания полностью отвязаны от процессов.
пример кода к указанному выше требованию.
Код
MODULATOR_INT:
    ____OCR_REG_WRITE MOD_PWM;1
    lds    Zh,ADR_MOD_TABLE_HIGH;2
    mov    Zl,MOD_ADR    ;1
    lpm    MOD_PWM,Z+    ;3     с инкрементом
    mov    MOD_ADR,Zl    ;1
    cpse     MOD_PWM,Zh    ;1/2     хитрость
    RETI            ;4  - 13 тактов SREG не портится, Z_reg портиться
далее код перезагрузки указателей на следующий сигнал

В реализиции "AVRasmOS.zip" на любой евент может быть назначена любая комбинация задач.
ну а в прерывании ____EVENT_SET и в путь smile.gif
просто лишний переход RCALL (как у Вас) и последющие вычисления требует (ИМХО навскидку) многовато тактов и не способствуют универсализиции, той что не потребует много накладных расходов.
bodja74
Цитата(yod @ Oct 27 2006, 06:42) *
Я пока "не догнал", как там "матеро" можно его использовать, но будем думать. Там еще проблема.
Я регистр Z оставил для пользования прерываниям - такая специфика - надо каждые __256 тактов__ _гарантировано_ обновить ШИМ-регистр таймера. А все значения в таблице прописаны во флэш.
таблица 1кб smile.gif

Ну например сделать свой программный стек и сохранять адресс возврата из прерваной задачи.

Цитата
ИМХО напрасно, если писать что-то тяжелее "эха" на уарте.

Я про реалтайм,тяжко будет поцепить на такую ось допустим генератор тестового видеосигнала,где потребуется прервание через каждые 64 микросекунды с наивысшим приоритетом.
Цитата
Идею понял. Поздравляю, редкостное извращение smile.gif
Я придерживаюсь тех позиций что прерывание должно отработать максимально быстро.
И прерывания полностью отвязаны от процессов.
пример кода к указанному выше требованию.
Код
MODULATOR_INT:
    ____OCR_REG_WRITE MOD_PWM;1
    lds    Zh,ADR_MOD_TABLE_HIGH;2
    mov    Zl,MOD_ADR;1
    lpm    MOD_PWM,Z+;3     с инкрементом
    mov    MOD_ADR,Zl;1
    cpse     MOD_PWM,Zh;1/2     хитрость
    RETI        ;4  - 13 тактов SREG не портится, Z_reg портиться
далее код перезагрузки указателей на следующий сигнал

В реализиции "AVRasmOS.zip" на любой евент может быть назначена любая комбинация задач.
ну а в прерывании ____EVENT_SET и в путь smile.gif
просто лишний переход RCALL (как у Вас) и последющие вычисления требует (ИМХО навскидку) многовато тактов и не способствуют универсализиции, той что не потребует много накладных расходов.

Хе,ну я уже говорил,я легко вцепил к своему осцилу генератор видео,без лишних телодвижений.
Вот "скоростной" вариант планировщика прерываний
Код
PLAN:
   pop R16            ;Сдвигаем стек ,старший адресс вектора прерывания нам не нужен ,все равно 00
   pop R16            ;Сдвигаем стек и заносим младший в Р16
   in  R2,SREG       ;Сохраням СРЕГ в регистре
   lds ZH,(ramend-1);Заносим адресс начала выбранной проги
   lds ZL,(ramend)
   adc ZL,R16         ;Складываем адресс программы и адресс вектора
   brcc (PC+2)       ;если у нас флаг переноса значит ZH+1
   inc  ZH
   out SREG,R2      ;востанавливаем СРЕГ
   icall                   ;переходим на вектор прерывания

Переход на подпрограмму фиксировано увеличивается на 16 тактов для любого прерывания и любой программы smile.gif
Кто придумает короче поставлю пиво!!!
В основной программе и подрограммах не используются R2,R16,Z в прерывания можно использовать в качестве промежуточных. В (ramend-1) и ,(ramend) заносится адресс выбранной программы,естественно при иницилизации стека (ramend-2)

ЗЫ В принципе подобные подходы вравнивать ,тоже самое что сравнивать топор с колесом,но думаю кто интересуется многозадачностью будет из чего выбрать smile.gif
trofim
.def Zero = rxx ;любой регистр

clr Zero ; в инициализации нулевая константа часто используется


;adc ZL,R16 ;Складываем адресс программы и адресс вектора
; brcc (PC+2) ;если у нас флаг переноса значит ZH+1
; inc ZH

;в первой строке ошибка !!!

add zl,r16
adc zh,Zero

Кто придумает короче поставлю пиво!!!
bodja74
Цитата(trofim @ Oct 31 2006, 14:16) *
Кто придумает короче поставлю пиво!!!


a14.gif , cheers.gif , a14.gif

Заместь adc нужно add

В этом коде ,есть еще одна ошибка ,исправив ее,ускорим выполнение еще на 1такт. wink.gif
=GM=
Цитата(bodja74 @ Oct 31 2006, 13:02) *
Цитата(trofim @ Oct 31 2006, 14:16) *

Кто придумает короче поставлю пиво!!!

a14.gif , cheers.gif , a14.gif
Вместо adc нужно add
В этом коде ,есть еще одна ошибка ,исправив ее,ускорим выполнение еще на 1такт. wink.gif

Стек уже подправлен, поэтому надо вместо icall поставить ijmp. Выигрыш 1 такт.
Короче, с вас пиво, вас за язык никто не тянул(:-).
bodja74
Цитата(=GM= @ Oct 31 2006, 19:30) *
Цитата(bodja74 @ Oct 31 2006, 13:02) *

Цитата(trofim @ Oct 31 2006, 14:16) *

Кто придумает короче поставлю пиво!!!

a14.gif , cheers.gif , a14.gif
Вместо adc нужно add
В этом коде ,есть еще одна ошибка ,исправив ее,ускорим выполнение еще на 1такт. wink.gif

Стек уже подправлен, поэтому надо вместо icall поставить ijmp. Выигрыш 1 такт.
Короче, с вас пиво, вас за язык никто не тянул(:-).


Абсолютно верно,ДВА ПИВА!!! smile.gif
a14.gif , cheers.gif,cheers.gif , a14.gif
=GM=
Цитата(bodja74 @ Oct 31 2006, 16:40) *
Абсолютно верно,ДВА ПИВА!!! smile.gifa14.gif , cheers.gif,cheers.gif , a14.gif

Ну ладно, при случае(:-). А вы где территориально?

Кстати, почему изначально 16 тактов было, должно вроде быть 17(:-).

Вот еще подумал, используются три регистра, не гуд. А мы знаем, что первый pop r16 заносит гарантированный ноль. Если написать так
Код
[font=Courier New]
PLANi: pop  r2            ;старший байт адреса вектора не нужен
       pop  r2            ;сдвигаем стек и заносим младший
       lds  ZH,(ramend-1) ;адрес начала выбранной проги
       lds  ZL,(ramend)
       in   r2,SREG       ;cохраням SREG
       sbiw zl,-vector(i) ;реальный адрес вектора
       out SREG,R2        ;востанавливаем SREG
       ijmp
[/font=Courier New]

то будет использоваться всего один регистр r2. Но, конечно, под каждое i-ое прерывание должна быть своя подрограмма PLANi и своя константа vector(i) (1, 2, 3, …)
bodja74
Цитата(=GM= @ Oct 31 2006, 20:41) *
Ну ладно, при случае(:-). А вы где территориально?

Украина,Бердичев
Цитата
Кстати, почему изначально 16 тактов было, должно вроде быть 17(:-).

Команда brcc (PC+2) выполняется за 1 такт если условие не выполняется ,и за 2 если перепрыгивает.
хотя если учесть, что заменяется в векторах jmp(rjmp) на call(rcall) тогда +1
Цитата
Вот еще подумал, используются три регистра, не гуд. А мы знаем, что первый pop r16 заносит гарантированный ноль. Если написать так
Код
[font=Courier New]
PLANi: pop  r2        ;старший байт адреса вектора не нужен
       pop  r2        ;сдвигаем стек и заносим младший
       lds  ZH,(ramend-1);адрес начала выбранной проги
       lds  ZL,(ramend)
       in   r2,SREG      ;cохраням SREG
       sbiw zl,-vector(i);реальный адрес вектора
       out SREG,R2    ;востанавливаем SREG
       ijmp
[/font=Courier New]

то будет использоваться всего один регистр r2. Но, конечно, под каждое i-ое прерывание должна быть своя подрограмма PLANi и своя константа vector(i) (1, 2, 3, …)

Ну раз такая жара ,тогда в векторах ставим
.org 0
jmp(rjmp) PLAN_START
jmp(rjmp) PLAN1
jmp(rjmp) PLAN2
....

и сносим pop R2
в результате
Код
PLANi:
       lds  ZH,(ramend-1);адрес начала выбранной проги
       lds  ZL,(ramend)
       in   r2,SREG      ;cохраням SREG
       adiw zl,vector(i);реальный адрес вектора
       out SREG,R2    ;востанавливаем SREG
       ijmp

Хотя перспектива включения такого кода для каждого прерывания меня не очень радует,но звание "сверхскоростного" он заслуживает. smile.gif
=GM=
GM Ну ладно, при случае(:-). А вы где территориально?

bodja74 Украина,Бердичев

Ясенько, вам пора музей контрабандистов открывать(:-). Еще вроде там венчание Бальзака было, в костеле...А Никольская церковь стоит?

bodja74 Ну раз такая жара, тогда в векторах ставим
.org 0
jmp(rjmp) PLAN_START
jmp(rjmp) PLAN1
jmp(rjmp) PLAN2
....
и сносим pop R2
в результате
Код
PLANi:  lds  ZH,(ramend-1) ;адрес начала выбранной проги
        lds  ZL,(ramend)
        in   r2,SREG       ;cохраням SREG
        adiw zl,vector(i)  ;реальный адрес вектора
        out  SREG,R2       ;востанавливаем SREG
        ijmp

bodja74 Хотя перспектива включения такого кода для каждого прерывания меня не очень радует, но звание "сверхскоростного" он заслуживает smile.gif

Ну раз пошла такая пьянка... Если хранить адрес проги не в памяти, а в регистрах (r5-r4), то получим суперсверхскоростной код(:-)
Код
PLANi:  in   r2,SREG       ;cохраням SREG
        movw r30,r4        ;current program address
        adiw zl,vector(i)  ;реальный адрес вектора
        out  SREG,R2       ;востанавливаем SREG
        ijmp

Ужоснах, если сравнивать с исходным кодом(:-). Всего 5 слов кода на прерывание, не так уж и много для вставки. Зато скорость удвоилась: 10 тактов по сравнению с 20 исходными! Это ж скока пива! Не, я стока не выпью(:-)
bodja74
Цитата(=GM= @ Nov 1 2006, 14:07) *
GM Ну ладно, при случае(:-). А вы где территориально?

bodja74 Украина,Бердичев

Ясенько, вам пора музей контрабандистов открывать(:-). Еще вроде там венчание Бальзака было, в костеле...А Никольская церковь стоит?

Ааа,бывали в наших краях,будете проезжать ,свистните,пивка попьем smile.gif

Цитата
Ну раз пошла такая пьянка... Если хранить адрес проги не в памяти, а в регистрах (r5-r4), то получим суперсверхскоростной код(:-)
Код
PLANi:  in   r2,SREG      ;cохраням SREG
        movw r30,r4    ;current program address
        adiw zl,vector(i) ;реальный адрес вектора
        out  SREG,R2      ;востанавливаем SREG
        ijmp

Ужоснах, если сравнивать с исходным кодом(:-). Всего 5 слов кода на прерывание, не так уж и много для вставки. Зато скорость удвоилась: 10 тактов по сравнению с 20 исходными! Это ж скока пива! Не, я стока не выпью(:-)


ПЬЕМ ПИВО ДАЛЬШЕ!!!
Как вам такой вариант?
Код
.org 0
jmp PLAN_START
ldi R16,$04
rjmp PLAN
ldi R16,$08
rjmp PLAN
....

clr R17

....

PLAN:  in   r2,SREG        ;cохраням SREG
    movw zl,r4 ;current program address
                add zl,r16
                adc zh,r17
    out  SREG,R2      ;востанавливаем SREG
    ijmp


Очередной изврат заключается в подмене в векторах одной команды jmp (4байта) на ldi и rjmp (по 2 байта) ,правда такой номер не пройдет для 8 и 8535 мег,но это не смертельно учитывая ,что у них не так немного памяти для размещение нескольких программ.
Естественно планировщик в первых 2кило кода ,скорость таже,но зато не нужно дублировать PLAN для каждого прерывания smile.gif

ЗЫ ГЫ,неслабая ось получилась smile.gifsmile.gifsmile.gif
=GM=
[quote name='bodja74' date='Nov 1 2006, 16:41' post='171352']
Ааа,бывали в наших краях,будете проезжать ,свистните,пивка попьем smile.gif
[/quote]
Замётано(:-)

Ну раз пошла такая пьянка... Если хранить адрес проги не в памяти, а в регистрах (r5-r4), то получим суперсверхскоростной код(:-)
Код
PLANi:  in   r2,SREG     ;cохраняем SREG
        movw r30,r4;current program address
        adiw zl,vector(i);реальный адрес вектора
        out  SREG,R2     ;востанавливаем SREG
        ijmp

Ужоснах, если сравнивать с исходным кодом(:-). Всего 5 слов кода на прерывание, не так уж и много для вставки. Зато скорость удвоилась: 10 тактов по сравнению с 20 исходными! Это ж скока пива! Не, я стока не выпью(:-)
[/quote]

ПЬЕМ ПИВО ДАЛЬШЕ!!!
Как вам такой вариант?
Код
.org 0
jmp PLAN_START
ldi R16,$04
rjmp PLAN
ldi R16,$08
rjmp PLAN
....

clr R17

....

PLAN:  in   r2,SREG    ;cохраням SREG
    movw zl,r4;current program address
                add zl,r16
                adc zh,r17
    out  SREG,R2     ;востанавливаем SREG
    ijmp


Очередной изврат заключается в подмене в векторах одной команды jmp (4байта) на ldi и rjmp (по 2 байта) ,правда такой номер не пройдет для 8 и 8535 мег,но это не смертельно учитывая ,что у них не так немного памяти для размещение нескольких программ.
Естественно планировщик в первых 2кило кода ,скорость таже,но зато не нужно дублировать PLAN для каждого прерывания smile.gif

ЗЫ ГЫ,неслабая ось получилась smile.gifsmile.gifsmile.gif
[/quote]

Тогда уж так, лишние регистры не помешают
Код
        ldi  r30,0x04
        rjmp PLAN

PLANi:  in   r2,SREG    ;cохраняем SREG
        clr  r31        ;current program address
        add  zl,r4      ;реальный адрес вектора
        adc  zh,r5
        out  SREG,R2    ;востанавливаем SREG
        ijmp


Почему бы и не попить(:-)
=GM=
Цитата(bodja74 @ Nov 1 2006, 16:41) *
ЗЫ ГЫ,неслабая ось получилась smile.gifsmile.gifsmile.gif


To bodja74

Довёл до 8 тактов с незначительными ограничениями.

Или уже неинтересно(:-(?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.