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

Контроллер потока внутри IRQ

Когда rtos ставить не хочется, а state машину внутри IRQ городить некрасиво, родилась мысль сделать механизм, который работает как yield(), но внутри IRQ. Т.е. выходит из IRQ, а при следующем вызове возобновляет исполнение с этого же места и сохраненным контекстом.

exec_task:
    push {r0-r11,lr}
    mov32 r1,#core_task_sp
    str sp,[r1]

    ldr sp, [r0]   ; sp=inline task stack
    pop {r0-r11,lr}
    bx lr

task_yield:
    push {r0-r11,lr} ; save inline task state
    mov r12,sp

    mov32 r0,#core_task_sp
    ldr sp,[r0]   ; sp=core stack

    pop {r0-r11,lr}
    str r12,[r0]
    bx lr

    end

      
int init_task(uint32_t* task_stack_top, void* routine) {
  task_stack_top[-1]=(int)routine;
  return (int)(task_stack_top-13);
  }

И простейший пример использования:

init:
  task_common_sp=init_task_fpu(TASK_COMMON_STACK_TOP, (void*)&thread_in_irq);

int result;
TransferComplete_IRQHandler() {
  result=DGRAM_SENT;
  // timeout irq cancel
  exec_task_fpu(&task_common_sp);
  }

Timeout_IRQHandler() {// во всех IRQ, которые вызывают один и тот же процесс, должен быть одинаковый приоритет
  result=TIMEOUT;
  // tx dma cancel
  exec_task_fpu(&task_common_sp);
  }

int send_dgram(char* dgram) {
  // ... запускаем отправку через DMA, устанавливаем irq таймаута
  task_yield();// ждём окончания отправки
  return result;
  }
int wait(int time) {
  // start_timeout_timer...
  task_yield();
  }


void thread_in_irq() {
  while (1) {
    int res=send_dgram("init");
    if (res==TIMEOUT) ...;
    wait(100);
    res=send_dgram("bbb");
    if (res==TIMEOUT) ...;
    wait(100);
    }
  }

Сделал реализацию, всё работает, но есть одно но. Стек для каждого такого процесса приходится выделять с учетом того, что могут вызываться вложенные IRQ. А RAM порой хочется поэкономить. Поэтому хочется во время вызова потока внутри IRQ использовать стек процесса (PSP). Но в handler mode можно пользоваться только MSP.

Думаю, как лучше сделать. Мысли такие:

- внутри IRQ переключаться в THREAD MODE и на стек PSP, а IRQ маскировать с помощью BASEPRI. Но тут вырисовывается проблемка в совместимости с RTOS:

RTOS для маскировки IRQ тоже использует BASEPRI (при некоторых вызовах callFromIRQ), но при выходе он BASEPRI сбрасывает в ноль (и ставит топорно, не проверяя что быдо до вызова), а не в то значение, которое было при вызове. Поэтому во всех IRQ нельзя будет пользоватся этим механизмом freertos на приоритетах >=процессного IRQ, либо сделать доработку для freertos, либо что-то еще придумать. Пока вырисовывается дополнительная опция freertos, добавляющая проверку и восстановление BASEPRI.

p.s. Предвидя сообщения в стиле "так никто не делает и вообще это не правильно", просьба воздержаться от этого и писать по существу. Ибо инструмент в работе показал себя очень хорошо (красивые реализации получаются если делать тайм-ауты и несколько источников возобновления исполнения), только хочется подружить его со RTOS.

 

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


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

3 minutes ago, jeka said:

Когда rtos ставить не хочется, ...

а очень хочется головняка на ровном месте :crazy:

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


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

Как раз наоборот. Когда начал им пользоваться, сам удивился насколько им приятно пользоваться. Ибо крайне компактный и быстрый, позволяющий в большинстве случаев обойтись без дерганья контекстов, сообщений, вейкапа процессов, очередей, как это бы делалось в RTOS (отсюда простота и выше скорость). И при этом код вполне понятный.

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


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

1 час назад, jeka сказал:

- внутри IRQ переключаться в THREAD MODE и на стек PSP, а IRQ маскировать с помощью BASEPRI.

Это еще как? Это делается только выходом из исключения/прерывания.
И, если честно, не догоняю в плюсы и удобства всего этого чудо-механизма.

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


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

Just now, Arlleex said:

Это делается только выходом из исключения/прерывания.

Почему только? PSR почему бы не подправить если очень хочется

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


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

27 минут назад, jeka сказал:

Почему только? PSR почему бы не подправить если очень хочется

А что конкретно Вы там подправить хотите, ну конечно, при условии, что очень хочется?

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


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

1 hour ago, jeka said:

Как раз наоборот. Когда начал им пользоваться, сам удивился насколько им приятно пользоваться. Ибо крайне компактный и быстрый, позволяющий в большинстве случаев обойтись без дерганья контекстов, сообщений, вейкапа процессов, очередей, как это бы делалось в RTOS (отсюда простота и выше скорость). И при этом код вполне понятный.

Пример задачи, где происходит моргание светодиода яркостью, пропорционально загрузке ядра. Чего тут сложного? Какие "дерганья контекста", "вейкапы", "очереди процессов"? Все просто как два пальца.

void ServiceLib::Thread::body()
{
    auto ledFlashPulseWidthMs = OS::getCoreUsage() * (ledFlashPediodMs / 100); // 100% - led is on allways
    
    if (ledFlashPulseWidthMs < 10) ledFlashPulseWidthMs = 10;
    
    pinLED.setToLow();
  
    sleep(ledFlashPulseWidthMs);
  
    pinLED.setToHigh();
  
    sleep(ledFlashPediodMs-ledFlashPulseWidthMs);
}

 

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


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

18 minutes ago, Arlleex said:

А что конкретно Вы там подправить хотите

IPSR bit definitions
Bits 8:0 ISR_NUMBER:
This is the number of the current exception:
0: Thread mode, все что не 0 - по логике handler mode

т.е. если ISR_NUMBER сбросить в 0, как я понимаю, процессор перейдет в thread mode и можно стек переключить на PSP. (в handler mode опытка переключения стека вызывает hardfault, а так бы было то что надо). Но нужно предварительно замаскировать более низкие прерывания, чтоб не нарушить логику работы контроллера прерываний. Как я понял, при изменении ISR_NUMBER NVIC сразу доступные для вызова приоритеты обновит. (может и ошибаюсь, поправьте меня кто знает)

 

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


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

1 минуту назад, jeka сказал:

т.е. если ISR_NUMBER сбросить в 0...

Ну я так и понял. Не получится. CPU игнорирует записи в это битовое поле.

4 минуты назад, jeka сказал:

в handler mode опытка переключения стека вызывает hardfault...

Не помню точно, но вроде никаких HF не будет. Процессор также игнорирует прямую смену стека в Handler Mode.

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


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

Just now, Arlleex said:

CPU игнорирует записи в это битовое поле.

жаль. (хотя я софт-ребут из IRQ делал и вроде всё сбрасывалось, но могу и ошибаться). Значит стек на возврат настроить и выполнить возврат, сразу в нужное место. А вот с восстановлением вопрос. Видимо так же.

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


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

6 минут назад, jeka сказал:

жаль. (хотя я софт-ребут из IRQ делал и вроде всё сбрасывалось, но могу и ошибаться)...

Софт-ребут делается совсем по-другому, посему не зависит от места, откуда его можно вызвать...

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


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

Софт-ресет можно эмулировать по-разному с разным аппаратным окружением при уйме разных условий.
Например, тупым зацикливанием программы с активным внутренним или внешним watchdog-ом.
Например, сбросом всех регистров CPU и периферии в их дефолтные значения и переходом по адресу ResetHandler().
Битами SYSRESETREQ и VECTRESET (в последнем случае подходить с головой и понимать, что происходит в системе).
Записью в GPIO, подключенного к RST МК... Да сколько всего можно придумать. Но никак не записью в IPSR.

Скорее всего, Вы что-то путаете:wink:

P.S. Единственное, где я еще могу оправдать посыл Ваших идей с идеологией контекстного вытеснения в ISR - энергосберегающие девайсы.
Пришло прерывание - отработали что-то по FSM, заснули. Но тут все от алгоритма работы зависит - не везде такой механизм удобен и оправдан.

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


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

17 minutes ago, Arlleex said:

Скорее всего, Вы что-то путаете

Всё верно. Я про ресет с помощью простой передачи управления по адресу. Запускал так бутлоадер прямо из irq. Ибо надо было не ресетить коммуникацию, иначе связь порвётся.

 

2 hours ago, Arlleex said:

если честно, не догоняю в плюсы и удобства всего этого чудо-механизма.

получается очень легкий механизм заточеный на событийную коммуникацию. Замена RTOS в части многопоточности и сигнализирования обслуживающих коммуникацию потоков. заменяя цепочку irq периферии->сообщение RTOS->wakeup процесса->IRQ планировщика->переключение контекста->обработка, на irq->переключение контекста->обработа

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


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

5 часов назад, jeka сказал:

Когда rtos ставить не хочется, а state машину внутри IRQ городить некрасиво, родилась мысль сделать механизм, который работает как yield(), но внутри IRQ. Т.е. выходит из IRQ, а при следующем вызове возобновляет исполнение с этого же места и сохраненным контекстом.

Ничего нового Вы не изобрели. Делали такое и работает давно уже в серийных устройствах (псевдозадача внутри IRQ со своим стеком и контекстом). И даже где-то на форуме я уже описывал такой способ как альтернативу машине состояний.

Только переключать режим процессора здесь - пустая затея. Если даже и возможная. Никаких бонусов не даёт.

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


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

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

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

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

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

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

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

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

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

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