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

Лютый баг в DMA STM32F4: кто-нибудь в курсе?

13 часов назад, Сергей Борщ сказал:

Почему у Arlleex не получилось - я не знаю, завтра на работе постараюсь найти время проверить на F407.

Не судьба - всех загнали на удаленку, дома F4 нет в наличии. 

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


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

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

Если поставить точку останова на u32 sr = USART1->SR, из консоли отправить 1 байт, затем сделать шаг - в sr будет виден установленный IDLE.
Если же поставить точку останова на if(sr & USART_SR_IDLE), то при отправке из консоли одного байта в sr будет виден сброшенный IDLE. Типа не успел еще.

Некорректный тест. Останов на брекпоинте - это уже другой режим функционирования процессора. Как там будет это обрабатываться UART-ом - одному ему известно, в мануале про это вроде нет инфы. Возможно что сам факт останова на бряке и ставит IDLE.

Если нужно знать как будет работать в нормальном режиме работы - никаких бряков.

Цитата

т.е. этим я хочу узнать, кто же возникает первым - RXNE или IDLE. После запуска программы, отправляю с консоли 1 символ - светодиод переключился. Это, получается, соответствует описанию в RM: после заливки прошивки в МК, пока перешел в консоль, пока нажал кнопку - прошло много времени, и RX-path зафиксировал простой линии (все 1 в битах). Когда я нажал отправку символа - я залетел в прерывание и увидел, что IDLE установлен, а RXNE еще нет - т.е. IDLE действительно срабатывает только тогда, когда обнаружен простой линии + старт-бит следующего символа.

Из теста не видно - когда у Вас установился IDLE. Может он установился сразу после инициализации порта и последующей паузы?

Имхо - нужно сперва сделать паузу, заведомо очистить статусы, разрешить только RXNEIE, потом отправить символ (на малой скорости, с высоким приоритетом прерывания; чтобы точно IDLE не успел установиться после RXNE из-за задержки входа в ISR); и прочитать статусы - и посмотреть в SR один только RXNE стоит или RXNE+IDLE? 

т.е.:

1. инит порта;

2. пауза в несколько символов как минимум;

3. очистка SR;

4. разрешить RXNEIE;

5. отправить символ;

6. прочитать SR в ISR.

 

1 час назад, Сергей Борщ сказал:

Не судьба - всех загнали на удаленку

Везёт вам. Двигатель+стенд домой не унесёшь. Придётся теперь по выходным на работу выходить...  :sad:

Уроды, блин!

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


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

В общем, ща буду GPIO-шками мерять последовательность установки флагов, а то я уже сам запутался:biggrin:

void USART1_IRQHandler(void)
{
  u32 sr = USART1->SR;
  
  if(sr & USART_SR_IDLE)
    GPIOG->ODR ^= 1 << 13,
    (void)USART1->DR;
  
  if(sr & USART_SR_RXNE)
    GPIOG->ODR ^= 1 << 14,
    (void)USART1->DR;
}


Осциллом гляну.

Итак, отправляю один символ. Судя по диаграмме, сначала взводится RXNE, затем IDLE. Скорость поставил 1200 бод.
Самое забавное: если символ 0x00, то "расстояние" между фронтами ровно 1 символ (~83 мс). Если 0xFF - 1 бит (~1 мс).

Отправляю несколько символов друг за другом - IDLE также устанавливается только после самого последнего.

Только вот возникает стойкое ощущение, что тут может возникнуть гонка состояний, особенно при работе с DMA. Насколько я помню, доступа к DR со стороны CPU при работающем DMA на эту периферию быть не должно... А IDLE сбрасывается чтением DR.

Почитал я еще мануалы на F4 и F0, и пришел к выводу, что документация там вообще на "высшем" уровне:rtfm:

F0:

Скрытый текст

image.thumb.png.2c38081949a853577afcbc76ed2f0e16.png

F4:

Скрытый текст

image.thumb.png.1985c373106bd652f20517644d31aba6.png

Да за такую документацию только руки поотрывать.

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


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

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

Итак, отправляю один символ. Судя по диаграмме, сначала взводится RXNE, затем IDLE. Скорость поставил 1200 бод.

Ну значит IDLE сигнализирует всё-таки о "таймауте отсутствия активности на линии". А не об "отсутствии активности сопровождаемой старт-битом".

Цитата

Самое забавное: если символ 0x00, то "расстояние" между фронтами ровно 1 символ (~83 мс). Если 0xFF - 1 бит (~1 мс).

Это говорит о механизме отслеживания IDLE: Значит он отслеживается не от момента завершения стоп-бита последнего символа, а от момента последнего фронта 0->1 на линии. Т.е. - самым простейшим способом.

 

PS: А эти тесты Вы делали при каком значении бита RWU? (про который я вчера писал). Влияет ли он на поведение IDLE?

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

Да за такую документацию только руки поотрывать.

Вы документацию Infineon не видели!  :biggrin: :aggressive:

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


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

17 минут назад, jcxz сказал:

А эти тесты Вы делали при каком значении бита RWU? (про который я вчера писал). Влияет ли он на поведение IDLE?

RWU = 0, при установке RWU и остальных прочих равных условиях поведение ровно такое же.
RWU полезен, скорее всего, тогда, когда нужно прекратить слушать входной поток, но до начала следующего кадра (в системе, где кадры - это разделенные по времени посылки). Например, для безопасного "горячего" подключения в линию.

Цитата

Вы документацию Infineon не видели!

Местами кривая тоже...

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


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

Вот как подобное описано в нормальной документации:

scr001.thumb.png.6fc526a5820a31c61de1d723efb3d40d.png

(c) TI.

Всё понятно - и когда и через сколько.

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


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

19.10.2021 в 22:50, jcxz сказал:

Переполнение буфера обнаруживается и обслуживается корректно (при необходимости запускаю DMA для очередного UART_RDMA_CHUNK не в основной буфер, а в отдельную "мусорку").

У Вас тот протокол поверх UART-а, я так полагаю, уже со всеми кадровыми разделителями и CRC? Иначе просто так перекинуть указатель снова на основной буфер с "мусорки" - маневр весьма опасный. У меня вот, например, протокол SLCAN, и там нет механизмов обеспечения проверки целостности кадров (да и сами "кадры" там условные). Для своей реализации, наверное, будет лишним обрабатывать переполнение (разве что вывести в отладочный порт сообщение, мол не хватает объема очереди/скорости парсинга и обработки).

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


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

9 минут назад, Arlleex сказал:

У Вас тот протокол поверх UART-а, я так полагаю, уже со всеми кадровыми разделителями и CRC? Иначе просто так перекинуть указатель снова на основной буфер с "мусорки" - маневр весьма опасный.

Нет. Зачем? Это ведь UART - поток байт. Протокол (ходящий по нему) он у меня уже в задаче ОС обрабатывается. Разгребающей полученное от ISR/DMA из очереди.

Точнее - протоколы, так как по разным UART-ам - разные протоколы, в то время как драйвер UART - единый.

И ничего опасного: ISR, сделавший такое переключение, просто устанавливает "флаг переполнения". Разгребающая задача - обрабатывает этот флаг вместе с данными FIFO. Точно так же, как если бы она читала данные из аппаратного FIFO и обрабатывала их совместно с флагом переполнения в регистре статуса UART. Ведь у вас не возникает проблем с обработкой аппаратного FIFO? А здесь - его аналог, врукопашную на ISR/DMA.

 

Цитата

У меня вот, например, протокол SLCAN, и там нет механизмов обеспечения проверки целостности кадров (да и сами "кадры" там условные).

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

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


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

Ну здесь, ИМХО, как-то заморочено будет:smile:

image.thumb.png.0f029eb8fd85e6dbfa85f6fa5426272b.png

R, W - указатели чтения и записи. Цветами обозначены конкретные сообщения: обрамления кадров нет (ASCII-формат), CRC тоже. Весь буфер разбит на одинаковые куски по 8 байт (просто для демонстрации). Запускаем DMA, и не читаем из буфера (чтобы было переполнение). Как только пришло прерывание Transfer Complete, перепрограммируем указатель на следующий кусок памяти, и так далее. При заполнении 2-го (счет с 0) буфера логика перекидывает указатель на "мусорку", потому что когда заполнится 3-й буфер, двигать уже что-то куда-то будет поздно (да и в этой точке можно считать 3-й буфер потенциально переполненным, т.к. если DMA запонит его 7-й элемент, двигать W будет уже некуда). Теперь все приходящие символы будут записываться в мусорку. Как только R переместится как минимум на 8 байтов, неактивный указатель DMA можно будет перекинуть на начало этого кусочка. Собственно, в мусорке часть красного сообщения была затерта новыми приходящими данными (фиолетовый цвет). По идее, надо как-то сообщить разгребающему потоку конкретное место, где возникло переполнение, чтобы он откинул как красное, так и (возможно битый) кусок того нового, которое будет помещено туда где сейчас зеленый.

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


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

4 часа назад, Arlleex сказал:

Запускаем DMA, и не читаем из буфера (чтобы было переполнение).

Что Вы имеете в виду? Если запускаете DMA (DMA.RX), то это собственно запуск чтения. Или я не понял о чём речь....  :unknw:

4 часа назад, Arlleex сказал:

По идее, надо как-то сообщить разгребающему потоку конкретное место, где возникло переполнение

Зачем "место" сообщать? Какой смысл?

Принимающий процесс, вычитывая данные из FIFO.RX, собирает из них допустим кадр какого-то протокола (ну или на лету разбирает поток символов на элементарные лексемы). Когда выставляется флаг переполнения, то этот читающий поток просто переходит в состояние "не_синхронизирован". У меня это начальное состояние у всех приёмных потоков, в которое они переходят при старте работы или при возникновении ошибок (например переполнения). Переход из состояния "не_синхронизирован", в состояние "синхронизирован", происходит при обнаружении принимающим потоком границы кадра. Из "синхронизирован" уже можно переходить в состояние приёма кадра/лексемы.

Например:

для SLIP этой границей будет: SLIP_END (0xC0);

для COBS = 0;

для Modbus - пауза;

для каких-то ASCII-ориентированных протоколов (например AT-командный протокол): '\r' или '\n'

...

Обнаружили: переходим -> в состояние "синхронизирован", а при любой ошибке или после старта -> "не_синхронизирован". При переходе в "не_синхронизирован" из рабочих состояний приёмник сбрасывает все текущие накапливаемые переменные/буфера.

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


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

13 часов назад, jcxz сказал:

Что Вы имеете в виду? Если запускаете DMA (DMA.RX), то это собственно запуск чтения.

Запустили DMA на прием символов из UART в буфер. В прерываниях TC соответствующим образом корректируем указатель на следующий кусочек этого буфера (DMA в режиме двойного буфера, конечно же). Чтобы "подыграть" переполнение FIFO, нужно всего лишь не читать из этого буфера из процесса чтения (т.е. не двигать указатель R на картинке).

Цитата

При переходе в "не_синхронизирован" из рабочих состояний приёмник сбрасывает все текущие накапливаемые переменные/буфера...

Ведь потеряем же все накопленные в буфере данные. Смотрите, что я имею в виду: смоделируем ситуацию, когда в буфер прилетает поток байтов, а процесс вычитки из FIFO и обработки протокола тупит и вовремя не обрабатывает его. Зеленое сообщение поместилось в буфер полностью, красное - нет, поэтому W встал туда где он нарисован, а хвост красного сообщения улетел в "мусорку". Следом за красным прилетело еще одно сообщение, поток байтов которого обозначен синим цветом. Он также разместился в "мусорке" (потому что рабочий FIFO все еще полон)

image.thumb.png.deeea70b65a99fb057c29d2dc9486592.png

И вот читающий процесс все-таки раздуплился и начинает читать; для примера, пусть он вычитает и обработает зеленое сообщение. Как только в рабочем буфере FIFO стало достаточно места, чтобы передвинуть указатели с "мусорки" на него, принимаемые байты (для примера сообщения, помеченного фиолетовым цветом) будут размещаться уже в нем

image.thumb.png.1c872b75929bdbe5585769889ac1821a.png

И вот засада: принимающий процесс видит, что в буфере есть байты, вычитывает их и синхронизируется с красным сообщением (распознает его начало). Только вот принимающий процесс не знает, что 2-байтовый обрубок конца красного сообщения валяется в "мусорке", а те 2 байта, что сейчас в качестве хвоста, это есть часть фиолетового сообщения. Грубо говоря, в последних байтах красного и фиолетового были эти самые '\r'. А в предпоследних - разные данные.

Здесь спасла бы контрольная сумма, заложенная в протоколе поверх этого UART, например. А если делать глобальный флажок "FIFO переполнен", по которому приемный процесс должен сделать очистку этого FIFO, то мы таким образом потеряем все сообщения, в том числе те, которые разместились в буфере без проблем. А в SLCAN сообщения, как правило, коротенькие довольно - от единиц до пару десятков байтов. А буфер я хочу сделать в пару кБ хотя бы, а то и 8:smile: Терять эти сообщения очень очень жалко.

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


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

2 часа назад, Arlleex сказал:

Здесь спасла бы контрольная сумма, заложенная в протоколе поверх этого UART, например. А если делать глобальный флажок "FIFO переполнен", по которому приемный процесс должен сделать очистку этого FIFO, то мы таким образом потеряем все сообщения, в том числе те, которые разместились в буфере без проблем.

Ну и пусть потеряем. Ведь переполнение - это же нештатная ситуация. Которая не должна происходить в нормальном режиме работы программы. А может происходить только из-за каких-то нештатных режимов функционирования, например: останов под отладчиком в процессе отладки и т.п. Либо ещё какие-то особые ситуации. И корректная её обработка должна заключаться только в том, чтобы исключить разрушения памяти, выполнить корректную очистку, перевод всех автоматов состояний в исходную фазу и т.п. И чтобы после неё происходило корректное восстановление в штатный режим. Чтобы программа не повисла, грубо говоря.

Если она у Вас происходит при обычной работе программы, то надо перепроектировать алгоритм работы так, чтобы этого не было. А не пытаться костылить. То что Вы предлагаете - это лечение симптомов болезни, а не самой болезни (исправление алгоритма).

Да и не получится ничего из этого, потому как если уж переполнилось (не из-за того, что кривой код, а потому что не хватает производительности МК), то всякими контрольными суммами, их вычислениями, ситуацию только усугубите.

 

PS: 8кБ - это даже если у Вас 1Мбод по CAN, всё равно нужно около 80мсек для полного заполнения. За это время можно воробья в поле загонять. Почему же времени не хватает на нормальную обработку?

2 часа назад, Arlleex сказал:

процесс вычитки из FIFO и обработки протокола тупит и вовремя не обрабатывает его.

Вот это и надо исправлять. А не костыли мастырить.

 

PS: А вообще конечно можно не общий флаг переполнения использовать, а массив флагов, количеством равным количеству DMA-сегментов. Читающий процесс тогда должен считывать не по-символьно, а по-сегментно: сегмент данных с принадлежащим ему флагом/флагами.

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


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

12 часов назад, jcxz сказал:

А вообще конечно можно не общий флаг переполнения использовать, а массив флагов, количеством равным количеству DMA-сегментов...

Вот мне такой механизм как-то больше по душе, думал над ним даже:smile: Я допускаю потерю пары-тройки уже принятых в буфер сообщений (это какие-то проценты от объема общего буфера) при нештатной ситуации. Можно развить идею с массивом флагов следующим образом. Функция чтения имеет прототип

// buf - куда читать
// len - сколько читать
// return: сколько считалось
u32 read(u8 buf[], u32 len);

В чем замысел. Поток обработки периодически выполняет примерно следующие действия

while(1)
{
  ...
  static u8 buf[BUF_SIZE];     // промежуточный буфер парсера
  ...
  u32 fifoBsy = fifo.getBsy(); // считали, сколько занято в буфере
  if(fifoBsy > 0)
  {
    u32 len = fifoBsy <= BUF_SIZE ? fifoBsy : BUF_SIZE,
        br  = fifo.read(buf, len);
    
    ...                        // тут, собственно, обработка
  }
}

Так пусть read() возвращает количество, запрошенное в len, но с учетом флага переполнения сегмента, из которого он будет читать. Т.е. драйвер установил флаг переполнения для сегмента 3, например. Мы запросили 32 байта, а нам read() выплюнул 8 всего лишь. По факту самого различия len и br можно судить о том, что поток в 32 байта, который сейчас в буфере - нифига не сплошной (т.е. нормальный ход заполнения буфера из UART был нарушен - к примеру, тем самым "сливом" в "мусорку"). Так что мы обрабатываем эти 8 байтов. Если среди них есть вполне законченные сообщения - ок. А если же парсер видит, что сообщение не закончено - переводим его автомат в состояние "несинхронизирован". Сообщение будет отброшено. Дальше по кругу снова вызывается ... read(). Даже если у следующего кусочка будет мусор в начале - несинхронизированный парсер не будет его воспринимать. Если прям вот хочется уметь считывать ровно столько сколько указали (если в буфере столько есть, конечно же), можно перегрузить read() или сделать этот метод с параметром.

Ахринеть как круто я придумал:biggrin: Осталось написать.

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


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

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

Ахринеть как круто я придумал:biggrin: Осталось написать.

Вы этим усложнением алгоритма и урезанием буфера (если кусочки по 8 байт, то на 8кБ наберётся 1кБ флагов!) ещё больше нагрузите парсер. И если он раньше терял, то так он будет ещё больше терять. Лучше этот 1кБ добавить к объёму очереди. И разобраться - почему у Вас парсер тормозит? И исправить это.

Неужто Ваша задача чем-то занята целых 80мсек(!), что не успевает разобрать очередь? (или даже больше 80мс? ведь это я предположил худший случай ==1Мбод, но Вы скорость не указали). Когда такие долгие таймауты у задач, то имхо - нужно разбираться почему они такие долгие.

Затем начнутся проблемы с порядком следования сообщений: хорошо если они совершенно независимые, но когда окажется, что одно сообщение зависит от другого, или важен порядок их следования - тут и выйдут боком все эти костыли с выборочным бракованием сообщений.  :unknw:

 

Не надо бороться со следствием, надо причину исправить. имхо.

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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