|
|
  |
Глобальное прерывание, возможно ли? |
|
|
|
Nov 5 2009, 13:32
|
Частый гость
 
Группа: Свой
Сообщений: 187
Регистрация: 22-06-05
Из: Минск, Беларусь
Пользователь №: 6 213

|
В очередной раз натолкнулся на проблему. В этот раз связанную с прерываниями.
В частности, для обмена через CAN, я организовал очередь сообщений, которая пополняется вызовом метода AddMessage, а освобождается по прерыванию от CAN. Проблема возникла из-за того, что и метод AddMessage и обработчик модифицируют одну и ту же область памяти, поэтому встречается случай когда они пытаются сделать это одновременно, а это недопустимо. Как одно из решений - это запретить прерывание на момент работы метода AddMessage, однако я не смог найти флаг глобального разрешения и запрещения прерывания. Попытка "игры" с флагами прерывания CAN приводит к тому, что события происходящие во время запрета прерываний, не фиксируются вовсе, т.е. в этом процессоре флаги прерывания выставляются только если установлено разрешение по прерыванию.
Есть ли решение в этой ситуации или надо полностью менять подход при работе с очередью?
|
|
|
|
|
Nov 5 2009, 15:25
|
Местный
  
Группа: Свой
Сообщений: 263
Регистрация: 2-02-07
Из: CN, Ukraine
Пользователь №: 24 970

|
Позволю себе подправить - запоминает текущее состояние контроллера прерывания, а главное - pending interrupts. И при выходе из секции делает текущему OR с сохраненным. Автору топика - вообще почитайте внимательно про ядро ARM и контроллер прерываний в вашем конкретном Атлоне  - сэкономите массу времени и анальгина в будущем.
|
|
|
|
|
Nov 6 2009, 16:15
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Yaumen @ Nov 5 2009, 15:32)  Есть ли решение в этой ситуации или надо полностью менять подход при работе с очередью? Очереди (направленные очереди если быть точнее) можно организовать так, что запрет прерываний непотребуется. Для этого необходимо и достаточно чтобы к очереди доступ имели не более двух потоков, один строго пишет, второй строго читает. Как раз Ваш случай. Код typedef struct tagQUEUE { volatile int put; volatile int get; int cnt; PVOID *pStorage; } TQUEUE, *PQUEUE;
void queue_define( PQUEUE pq, PVOID storage, int storage_size) { pq->cnt = storage_size / sizeof( PVOID ); pq->put = pq->get = 0; pq->pStorage = storage; }
PVOID queue_read( PQUEUE pq ) { PVOID retval = NULL; if (pq->get != pq->put) { // got smth to read int new_index = pq->get + 1; if (new_index >= pq->cnt) new_index = 0; retval = pq->pStorage[ pq->get ]; pq->get = new_index; } return retval; }
void queue_write( PQUEUE pq, PVOID msg ) { int new_index = pq->put + 1; if (new_index >= pq->cnt) new_index = 0; pq->pStorage[ pq->put ] = msg; // <-- эта операция защищена, т.к. индекс put еще не изменился, функция чтения не знает о наличии новой записи pq->put = new_index; // <-- здесь требуется атомарная модификация индекса, т.е. чтобы переменная put записалась одной асм командой (отсюда переменная put не может быть разрядностью больше чем разрядность регистров процессора). }
|
|
|
|
|
Nov 6 2009, 16:30
|
Профессионал
    
Группа: Свой
Сообщений: 1 481
Регистрация: 10-04-05
Пользователь №: 4 007

|
Цитата(defunct @ Nov 6 2009, 19:15)  Очереди (направленные очереди если быть точнее) можно организовать так, что запрет прерываний непотребуется. У автора нет никаких очередей. Где-то в цикле в main() чего обрабатывается, а по прерыванию - заполняется. При таком подходе поможет только механизм критических секций. Цитата(_dem) Позволю себе подправить - запоминает текущее состояние контроллера прерывания, а главное - pending interrupts. И при выходе из секции делает текущему OR с сохраненным. Если запрещать/восстанавливать глобальное прерывание на уровне процессора, то в контроллере прерываний все автоматом запоминается/выполняется и нет никакого смысла на уровне контроллера прерываний усложнять себе жизнь.
|
|
|
|
|
Nov 6 2009, 16:41
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(sergeeff @ Nov 6 2009, 18:30)  У автора нет никаких очередей. Будьте внимательнее, в самом первом посте написано: Цитата я организовал очередь сообщений, которая пополняется вызовом метода AddMessage А по поводу Цитата Где-то в цикле в main() чего обрабатывается, а по прерыванию - заполняется. При таком подходе поможет только механизм критических секций. см. код выше. Гарантирую что он будет работать так как надо без крит секций и запрета прерываний.
|
|
|
|
|
Nov 6 2009, 17:07
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(sergeeff @ Nov 6 2009, 19:04)  А как вы собираетесь добиться атомарности в операции модификация индекса без запрета прерываний? там написано: (отсюда переменная put не может быть разрядностью больше чем разрядность регистров процессора) к get это тоже относится Цитата И все зависит от того как конкретный компилятор при определенных опциях оттранслирует это присваивание. Не важно сколько команд уйдет на выполнение Сишной конструкции pq->put = new_index; важно, чтобы именно запись (STR) осуществилась атомарно. т.е. чтобы переменная put ни при каких условиях не оказалась в памяти "частично модифицированной". А для этого достаточно сделать ее шириной в регистр проца и пользовать соответвующее выравнивание (чтобы компилятор не дробил инструкцию записи напр STR на несколько инструкций STRB).
|
|
|
|
|
Nov 10 2009, 07:13
|
Частый гость
 
Группа: Свой
Сообщений: 187
Регистрация: 22-06-05
Из: Минск, Беларусь
Пользователь №: 6 213

|
Я извиняюсь, за отсутствие в собственно созданной теме. Тема была перенесена модератором и я не сразу сообразил что произошло и куда перенеслось.
Теперь относитьно вопроса. Я сейчас работаю с LPC2366 поэтому этот вопрос касался именно его, понятно, что этого в самом сообщении не видно, поэтому так же извиняюсь. Не думал, что мой вопрос вызовет такой резонанс. Действительно проблема состояла в том, чтобы обезопасить код работающий в основном цикле, от возможных изменений, производимых в обработчике сообщений. Так как все это время, я думал что эта тема была удалена, я постарался решить вопрос теми средствами о которых знал. Вопрос с запретом прерываний решил на уровне контроллера прерывания с помощью модификаций регистров VICIntEnable и VICIntEnClear, вроде бы работает. Что касается самой очереди, то тут не так все просто. Возможно ее и очередью то трудно назвать, просто это наиболее близкое по смыслу название, которое я смог придумать. Суть проще рассказать на примере. Сейчас "очередь" используется при работе с CAN. ID CAN побитово разбит так, что в нем уживаются ID кому, ID от кого и индекс фрагмента сообщения (для сообщений с длиной более 8-ми байт). Передающее устройство разбивает большое сообщение на фрагменты по 8-м байт и передает. Приемное устройство принимает и ориентируясь на IDs и индексы собирает сообщение. Так как на шине могут быть несколько передатчиков, то для каждого из них создается свой минибуферчик в общем большом массиве, выделенном для приема сообщений по CAN. После окончания приема по прерываниям, основной цикл забирает сообщение, которое необязательно может находиться в начале "очереди", при этом весь остальной массив перестраивается. Так вот чтобы индексация не нарушалась и требовалось запретить прерывание на время пока будет массив перестраиваться.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|