|
STM32F0x2 и USB double-buffer, ошибки flow-control на double-buffered bulk-in endpoint |
|
|
|
Feb 22 2017, 19:36
|

Гуру
     
Группа: Админы
Сообщений: 3 621
Регистрация: 18-10-04
Из: Москва
Пользователь №: 904

|
Имеется кит на базе STM32F072 (на STM32F042 те же проблемы), на котором отлаживается прошивка USB-device с двумя bulk endpoint'ами (in/out). В режиме single-buffer все работает штатно: передача идет только при установке RX_VALID/TX_VALID в соответствующих битах регистров USB_EPnR. Переключение управляющих битов в этих регистрах происходит в соответствии с их описанием в RM0091 (toggling). После успешной отладки в этом режиме перешли к использованию режима double-buffer и с ним начались неожиданные проблемы: при первой передаче по Bulk-in endpoint происходит неконтролируемая отправка второго пакета. Суть эксперимента в следующем: 1. устройство подключается к хосту, успешно проходит энумерацию, чтение дескрипторов и т.п. (lsusb -vvv показывает все правильно); 2. в устройстве активируется bulk-in endpoint в режиме double buffer и SW_BUF/DTOG_TX биты ставятся в положение 1/0, настраиваются адреса буферов для передачи в памяти пакетов (два буфера), данные для передачи записываются в область памяти первого буфера и его счетчик передачи устанавливается равным 60. При этом второй буфер в памяти пакетов заполняется характерной сигнатурой и его размер устанавливается равным 11 (это несущественно). Статус endpoint устанавливается равным TX_VALID; 3. на хосте программа в цикле с помощью libusb пытается выполнить передачи блоков данных размером 64 байта (чтение из устройства, bulk in); 4. запускаем программу и получаем два буфера данных, вместо одного (60 и 11 байт). При этом второй буфер передается несмотря на то, что после передачи первого буфера SW_BUF/DTOG_TX == 11, т.е. согласно описанию передача должна приостановиться до момента, когда SW_BUF/DTOG_TX станут равны 01 соответственно. Получается, что контроллер отправляет второй буфер "авансом", несмотря на состояние специальных разрядов управления потоком передачи. На больших блоках данных, например, при работе с mass storage это незаметно, т.к. второй буфер используется сразу "по делу" и эта паразитная передача не мешает. Однако если, например, выполнять поллинг состояния устройства с помощью bulk-in endpoint, то второй пакет будет содержать неожиданные данные, которые не были еще сформированы программой в микроконтроллере. Интересно то, что после еще одной отправки, т.е. когда состояние SW_BUF/DTOG в вышеописанном примере перейдет из 01 в 00, передача честно остановится, контроллер будет отправлять NAK при попытке передачи данных через bulk-in и описанный в документации механизм flow control начинает работать согласно описанию. Но это не дает ответа, что делать со вторым блоком данных, который отправляется вопреки всему. В errata на этот счет никаких данных нет. В STM32CubeMX драйвер HAL PCD реализован из рук вон плохо в области работы с double-buffered endpoint и поэтому как источник информации о правильном методе работы в этом режиме служить не может. В других открытых библиотеках этого режима нет. С другой стороны опыт показывает, что разница в скорости передачи между single-buffered bulk-in endpoint и double-buffered не превышает 10% по нашим замерам (800 кБайт/с против 870 кБайт/с в тесте передачи блоков внутреннего флеша без использования DMA), поэтому применение этого режима трудно назвать критичным. Но все же интересно, у кого-нибудь он работает правильно и согласно описанию производителя?
--------------------
BR, Makc В недуге рождены, вскормлены тленом, подлежим распаду. (с) У.Фолкнер.
|
|
|
|
|
 |
Ответов
|
Feb 27 2017, 07:15
|
Знающий
   
Группа: Участник
Сообщений: 835
Регистрация: 9-08-08
Из: Санкт-Петербург
Пользователь №: 39 515

|
Я использовал DBL_BUF на F103, и всё работает нормально. В мануале, к сожалению, слишком куцое описание этого режима, а в HAL реализация двойных буферов, мне кажется, забагована. Баг в том, что обработчик прерывания должен уметь при необходимости обслуживать минимум два буфера за один вход в прерывание, если контроллер того требует. Отличие моего от вашего алгоритма в том, что я при инициализации заполнял SW_BUF/DTOG_TX нулями, и только потом, когда уже установлен бит DBL_BUF, переключал SW_BUF, чтобы начать передачу одного буфера, это различие может оказаться важно. UPD: Могу предположить, что у вас случилось: вы инициализировали SW_BUF/DTOG_TX в 1/0, потом заполнили буфер данными и переключили SW_BUF(в нуль). В таком случае контроллер совершенно справедливо отправит два буфера, просто из куцого мануала этого не понять.
Сообщение отредактировал Timmy - Feb 27 2017, 07:46
|
|
|
|
|
Feb 27 2017, 10:32
|

Гуру
     
Группа: Админы
Сообщений: 3 621
Регистрация: 18-10-04
Из: Москва
Пользователь №: 904

|
Цитата(Timmy @ Feb 27 2017, 10:15)  Я использовал DBL_BUF на F103, и всё работает нормально. В мануале, к сожалению, слишком куцое описание этого режима, а в HAL реализация двойных буферов, мне кажется, забагована. Баг в том, что обработчик прерывания должен уметь при необходимости обслуживать минимум два буфера за один вход в прерывание, если контроллер того требует. Да, там с этим проблема и по сути вместо подсовывания следующего буфера они там еще раз кладут тот же, что только что успешно передали. В этом нет смысла, только дополнительная задержка. Цитата Отличие моего от вашего алгоритма в том, что я при инициализации заполнял SW_BUF/DTOG_TX нулями, и только потом, когда уже установлен бит DBL_BUF, переключал SW_BUF, чтобы начать передачу одного буфера, это различие может оказаться важно. Только что попробовал изначально установить SW_BUF/DTOG_TX в 0/0, после чего DBL_BUF устанавливается в 1, записывается буфер размером 64 байта и переключается SW_BUF (из 0 в 1) для начала передачи. Со стороны хоста даётся запрос на 128 байт для Bulk-in endpoint и вместо ожидания приёма данных с вылетом по таймауту (нет короткого блока данных, завершающего передачу) я получаю 64 + содержимое из второго буфера, которого не ждал. Т.е. складывается впечатление, что начальное состояние этих бит не играет роли. Цитата UPD: Могу предположить, что у вас случилось: вы инициализировали SW_BUF/DTOG_TX в 1/0, потом заполнили буфер данными и переключили SW_BUF(в нуль). В таком случае контроллер совершенно справедливо отправит два буфера, просто из куцого мануала этого не понять. В документации у них нет информации, что контроллер может запоминать переключения. У них написано, что анализируется только комбинация управляющих разрядов регистра и при сочетании 1/1 или 0/0 в хост отдаётся NAK до момента переключения в 0/1 или 1/0 соответственно. А у меня выходит, что эта логика начинает работать только спустя одну "лишнюю" транзакцию, которая по номеру идёт второй и передаёт неподтверждённый буфер. После первого круга по буферам все начинает работать правильно. Но вот как решить проблему первого круга?
--------------------
BR, Makc В недуге рождены, вскормлены тленом, подлежим распаду. (с) У.Фолкнер.
|
|
|
|
|
Feb 28 2017, 04:49
|
Знающий
   
Группа: Участник
Сообщений: 835
Регистрация: 9-08-08
Из: Санкт-Петербург
Пользователь №: 39 515

|
Цитата(makc @ Feb 27 2017, 13:32)  Только что попробовал изначально установить SW_BUF/DTOG_TX в 0/0, после чего DBL_BUF устанавливается в 1, записывается буфер размером 64 байта и переключается SW_BUF (из 0 в 1) для начала передачи. Со стороны хоста даётся запрос на 128 байт для Bulk-in endpoint и вместо ожидания приёма данных с вылетом по таймауту (нет короткого блока данных, завершающего передачу) я получаю 64 + содержимое из второго буфера, которого не ждал. Т.е. складывается впечатление, что начальное состояние этих бит не играет роли. Было бы любопытно посмотреть на лог, описывающий последовательнось и времена переходов между состояниями регистра USB_EPxR. То есть программируете старт передачи, все прерывания должны быть всё время теста запрещены, и опрашиваете где-нибудь полсекунды USB_EPxR, при любых изменениях записывая в лог-вектор новое состояние и время изменения из системного таймера. А также сбрасывая CTR_TX, если он появится. Потом лог-вектор срисовать в отладчике или выплюнуть через консоль. Цитата(makc @ Feb 27 2017, 13:32)  В документации у них нет информации, что контроллер может запоминать переключения. У них написано, что анализируется только комбинация управляющих разрядов регистра и при сочетании 1/1 или 0/0 в хост отдаётся NAK до момента переключения в 0/1 или 1/0 соответственно. А у меня выходит, что эта логика начинает работать только спустя одну "лишнюю" транзакцию, которая по номеру идёт второй и передаёт неподтверждённый буфер. После первого круга по буферам все начинает работать правильно. Но вот как решить проблему первого круга?  В результате экспериментов я установил, что в USB контроллере есть скрытый бит, который определяет, как интерпретируются сочетания SW_BUF/DTOG 0/0 и 1/1 - либо как два пустых буфера, либо как два занятых. Этот бит приходится восстанавливать программно - после переключения SW_BUF - два занятых, после чтения единичного CTR_TX или начальной инициализации - два пустых. То есть в исходном состоянии 1/0 || 0/1 можно набить данными один свободный буфер, переключить SW_BUF, и новое состояние окажется 0/0 || 1/1, однако оно будет означать два занятых и готовых к передаче буфера, а в мануале про это ничего не написано.
|
|
|
|
|
Feb 28 2017, 07:48
|

Гуру
     
Группа: Админы
Сообщений: 3 621
Регистрация: 18-10-04
Из: Москва
Пользователь №: 904

|
Цитата(Timmy @ Feb 28 2017, 07:49)  Было бы любопытно посмотреть на лог, описывающий последовательнось и времена переходов между состояниями регистра USB_EPxR. То есть программируете старт передачи, все прерывания должны быть всё время теста запрещены, и опрашиваете где-нибудь полсекунды USB_EPxR, при любых изменениях записывая в лог-вектор новое состояние и время изменения из системного таймера. А также сбрасывая CTR_TX, если он появится. Потом лог-вектор срисовать в отладчике или выплюнуть через консоль. Отладка идёт через последовательный порт и даже на скорости 430 кБит/с сильно тормозит процесс передачи через bulk, по эксперимент попробую устроить. Цитата В результате экспериментов я установил, что в USB контроллере есть скрытый бит, который определяет, как интерпретируются сочетания SW_BUF/DTOG 0/0 и 1/1 - либо как два пустых буфера, либо как два занятых. Этот бит приходится восстанавливать программно - после переключения SW_BUF - два занятых, после чтения единичного CTR_TX или начальной инициализации - два пустых. То есть в исходном состоянии 1/0 || 0/1 можно набить данными один свободный буфер, переключить SW_BUF, и новое состояние окажется 0/0 || 1/1, однако оно будет означать два занятых и готовых к передаче буфера, а в мануале про это ничего не написано. Честно говоря я не понял, как можно инициализировать этот теневой бит, о котором Вы говорите. Как контроллер отличает переключения SW_BUF (DTOG_RX) в режиме инициализации от рабочего режима? По значению бита DBL_BUF? Как у Вас выглядит последовательность настройки конечной точки? Какие биты устанавливаются/сбрасываются и в каком порядке?
--------------------
BR, Makc В недуге рождены, вскормлены тленом, подлежим распаду. (с) У.Фолкнер.
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|