реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> STM32F407 Ethernet checksum = 0
cyrax0
сообщение Mar 10 2016, 09:29
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 22
Регистрация: 9-12-14
Пользователь №: 84 046



Всем добрый день!

Используется связка контроллеров STM32F407 с Ethernet, набортными свичами, lwip, аппаратной чексуммой и широковещательным udp-обменом. Время от времени происходит проблема: как показывает лог Вайршарка, один случайный контроллер из тех, которые посылают чаще, чем другие (порядка 200 пак./с.), вроде как перестает считать чексумму исходящих пакетов (ставит 0 в Header checksum и UDP checksum) или, реже, дает некорректную чек-сумму. Из-за этого другие теряют с ним связь. Этой ситуации предшествует обрезанный или склеенный с другим пакет (что странно, но не критично), после чего все пакеты следуют или без чек-суммы, или с некорректной. Ситуация может устраниться, может устраниться временно, может не устраниться - до передергивания Ethernet-кабеля, что через прерывание вызывает ETH_Stop/ETH_Start (stm32f4x7_eth.c), ETH_FlushTransmitFIFO и пр. (HAL-драйвер не используется). Вероятно, очищение fifo микросхемы каким-то образом и исправляет ситуацию.

Случайный сброс признаков аппаратной чексуммы в дескрипторах я исключил - задавал им 1 прямо перед выставлением признака OWN.
(Еще при нагрузочном тестировании зафиксировал попадание в лог редких одиночных пакетов с нулевой Header checksum, но с корректной UDP checksum и без левого пакета перед этим. Непонятно, связано ли это вообще с основной проблемой.)

Скажите, есть ли вероятность, что сама микросхема перестает корректно считать чек-сумму (потому что пакеты, в остальном, она выдает корректно)? Если нет, то куда копать: lwip, еще какая-то работа с dma? Даташит, само собой, я изучал, и ситуаций, подпадающих под нулевую crc, у нас вроде как нет.

Сообщение отредактировал cyrax0 - Mar 10 2016, 09:35
Go to the top of the page
 
+Quote Post
cyrax0
сообщение Mar 11 2016, 13:38
Сообщение #2


Участник
*

Группа: Участник
Сообщений: 22
Регистрация: 9-12-14
Пользователь №: 84 046



Изучая проблему, наткнулся на функцию ETH_Prepare_Transmit_Descriptors... Не видите ли вы в ней проблемы? В функции проверяется занятость текущего дескриптора, но если пакет делится на несколько буферов (т.е. пакет больше ETH_TX_BUF_SIZE), то второй и последующие могут затереть занятые ДМА буферы и дескрипторы?

CODE
/**
* @brief Prepares DMA Tx descriptors to transmit an ethernet frame
* @param FrameLength : length of the frame to send
* @retval error status
*/
uint32_t ETH_Prepare_Transmit_Descriptors(u16 FrameLength)
{
uint32_t buf_count =0, size=0,i=0;
__IO ETH_DMADESCTypeDef *DMATxDesc;

/* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
if((DMATxDescToSet->Status & ETH_DMATxDesc_OWN) != (u32)RESET)
{
/* Return ERROR: OWN bit set */
return ETH_ERROR;
}

DMATxDesc = DMATxDescToSet;

if (FrameLength > ETH_TX_BUF_SIZE)
{
buf_count = FrameLength/ETH_TX_BUF_SIZE;
if (FrameLength%ETH_TX_BUF_SIZE) buf_count++;
}
else buf_count =1;

if (buf_count ==1)
{
/*set LAST and FIRST segment */
DMATxDesc->Status |=ETH_DMATxDesc_FS|ETH_DMATxDesc_LS;
/* Set frame size */
DMATxDesc->ControlBufferSize = (FrameLength & ETH_DMATxDesc_TBS1);
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
DMATxDesc->Status |= ETH_DMATxDesc_OWN;
DMATxDesc= (ETH_DMADESCTypeDef *)(DMATxDesc->Buffer2NextDescAddr);
}
else
{
for (i=0; i< buf_count; i++)
{
/* Clear FIRST and LAST segment bits */
DMATxDesc->Status &= ~(ETH_DMATxDesc_FS | ETH_DMATxDesc_LS);

if (i==0)
{
/* Setting the first segment bit */
DMATxDesc->Status |= ETH_DMATxDesc_FS;
}

/* Program size */
DMATxDesc->ControlBufferSize = (ETH_TX_BUF_SIZE & ETH_DMATxDesc_TBS1);

if (i== (buf_count-1))
{
/* Setting the last segment bit */
DMATxDesc->Status |= ETH_DMATxDesc_LS;
size = FrameLength - (buf_count-1)*ETH_TX_BUF_SIZE;
DMATxDesc->ControlBufferSize = (size & ETH_DMATxDesc_TBS1);
}

/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
DMATxDesc->Status |= ETH_DMATxDesc_OWN;

DMATxDesc = (ETH_DMADESCTypeDef *)(DMATxDesc->Buffer2NextDescAddr);
}
}

DMATxDescToSet = DMATxDesc;

/* When Tx Buffer unavailable flag is set: clear it and resume transmission */
if ((ETH->DMASR & ETH_DMASR_TBUS) != (u32)RESET)
{
/* Clear TBUS ETHERNET DMA flag */
ETH->DMASR = ETH_DMASR_TBUS;
/* Resume DMA transmission*/
ETH->DMATPDR = 0;
}

/* Return SUCCESS */
return ETH_SUCCESS;
}


Сообщение отредактировал IgorKossak - Mar 11 2016, 18:32
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Mar 11 2016, 14:05
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Проблема есть, но:
- чему у Вас равен ETH_TX_BUF_SIZE? Возможна ли ситуация вообще?
- в известном мне порте lwip стоит еще одна проверка в low_level_output
Go to the top of the page
 
+Quote Post
scifi
сообщение Mar 12 2016, 09:02
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136



Цитата(cyrax0 @ Mar 11 2016, 16:38) *
Изучая проблему, наткнулся на функцию ETH_Prepare_Transmit_Descriptors... Не видите ли вы в ней проблемы?

Это STM наговнокодили? Ну тогда не удивительно, что глючит. У них всё глючит. Напишите драйвер Ethernet сами. Я так и сделал - ни разу глюков не замечал. Кстати, всего 500 строк, в отличие от.
Go to the top of the page
 
+Quote Post
pitt
сообщение Mar 12 2016, 16:34
Сообщение #5


Местный
***

Группа: Участник
Сообщений: 328
Регистрация: 1-06-06
Из: USA
Пользователь №: 17 672



Цитата(scifi @ Mar 12 2016, 04:02) *
Это STM наговнокодили? Ну тогда не удивительно, что глючит. У них всё глючит. Напишите драйвер Ethernet сами. Я так и сделал - ни разу глюков не замечал. Кстати, всего 500 строк, в отличие от.

Это такая бизнес-модель: как можно быстрее, чтобы нишу занять. Кроме того, их "код" на все возможные и нереальные комбинации, которые позволяет их железо. Я, как и Вы и многие другие написал и пишу свои драйвера, но они делают не все, а то что я или Вы или другие считают необходимым. Кроме того, это стоит времени, а оно есть не всегда и не у всех, вот и расплачиваются надежностью и качеством...
Я никого не оправдываю и ни в чем не убеждаю, просто принимая решение, делайте это осознанно.
Цитата
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, ST MICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.



--------------------
Прокричал немой глухому:"...Спасибо за внимание!"
http://www.youtube.com/watch?v=3Nnj4ky4Z_g
Go to the top of the page
 
+Quote Post
AleksBak
сообщение Mar 12 2016, 17:21
Сообщение #6


Частый гость
**

Группа: Участник
Сообщений: 132
Регистрация: 6-02-16
Из: г. Баку
Пользователь №: 90 364



Цитата(pitt @ Mar 12 2016, 20:34) *
...
Я никого не оправдываю и ни в чем не убеждаю, просто принимая решение, делайте это осознанно.

Не хотелось бы тоже их оправдывать, но то что они задумали с Кубом должно работать на стольких платах (!), что просто иногда удивляешься как оно работает. Вот тут например, для Эзернета, есть совсем разные PHY драйверы - например на плате STM32F746NG-DISCO и STM32F756-EVAL, но если посмотреть. то сгенерированный код написан спецом для EVAL платы (ведь она дороже в 10 раз!!! и на ней DP83848), но также работает и на плате DISCOVERY (он такой же будет и для этой платы) как ни странно!!! Хотя пишет в конце настройки драйвера всякую фигню в регистры ("думает", что настраивает расширенные регистры PHY драйвера - LAN8742A). Тоже самое касается и для др. семейств контроллеров. Посмотрите! У меня и для F107 точно такая ситуация - код для DP83848 (EVAL - 310C что-такое точно сейчас не помню) работает на PHY LAN8720A и как и писал (т.е. в конце всякую фигню пишет в драйвер). Но работает! Конечно это наверняка и может быть не относится к ТС, но все равно посмотреть стоит.
Go to the top of the page
 
+Quote Post
pitt
сообщение Mar 13 2016, 01:33
Сообщение #7


Местный
***

Группа: Участник
Сообщений: 328
Регистрация: 1-06-06
Из: USA
Пользователь №: 17 672



Цитата(AleksBak @ Mar 12 2016, 12:21) *
Не хотелось бы тоже их оправдывать, но ...

Во-первых,
Цитата
код, написан спецом
- это слишком громко сказано: код прекорявый и написан, или, может точнее, накакан отнюдь не профессионалом...
Во-вторых, они НЕ поддерживают различные PHY, они используют общие регистры, которые, по условию, одинаковы для всех PHY. Я пишу свои файлы поддержки PHY, потому как у них есть отличающиеся регистры, которые не нужны, если все работает, а вот когда не все ...
В-третьих, каждый сам определят что, для чего и как делать.


--------------------
Прокричал немой глухому:"...Спасибо за внимание!"
http://www.youtube.com/watch?v=3Nnj4ky4Z_g
Go to the top of the page
 
+Quote Post
AleksBak
сообщение Mar 13 2016, 04:38
Сообщение #8


Частый гость
**

Группа: Участник
Сообщений: 132
Регистрация: 6-02-16
Из: г. Баку
Пользователь №: 90 364



"спецом" - имел ввиду специально, а не написанный специалистом. :0
Go to the top of the page
 
+Quote Post
cyrax0
сообщение Mar 14 2016, 07:13
Сообщение #9


Участник
*

Группа: Участник
Сообщений: 22
Регистрация: 9-12-14
Пользователь №: 84 046



Цитата(aaarrr @ Mar 11 2016, 18:05) *
Проблема есть, но:
- чему у Вас равен ETH_TX_BUF_SIZE? Возможна ли ситуация вообще?


Большинство пакетов у нас небольшие, поэтому ETH_TX_BUF_SIZE = 256. Думаю, именно это и стало корнем проблемы.
У нас циклически посылается несколько пакетов, и только один из них > 256. Лог вайршарка показывает, что началом проблемы является некорректный пакет, состоящий из начала большого пакета (256 байт) и маленького пакета вместо хвоста большого. Налицо ошибка в обработке кольца дескрипторов. После этого для следующих пакетов Ethernet считать чексумму отказывается (хотя передаются они корректно). Сложно представить, что микросхема из-за посылки некорректного пакета перестает правильно считать чексумму; получается, что некорректные исходные данные из дескрипторов переводят ее в это состояние. Буду лопатить stm-овский код на предмет этого, свой писать сейчас (с учетом необходимости обкатки) возможности нет.

Цитата(aaarrr @ Mar 11 2016, 18:05) *
- в известном мне порте lwip стоит еще одна проверка в low_level_output

Верно, в low_level_output, которая вызывает ETH_Prepare_Transmit_Descriptors, тоже есть проверка на занятость текущего дескриптора - но опять же на один. Правильно будет именно в этой функции рассчитывать количество необходимых буферов и выходить, если оно меньше количества свободных (хотя по сути проблема иллюзорна: MAC с посылкой в while(1) способен выдавать 30000 пакетов в секунду, это гораздо больше, чем мы выдаем за цикл, так что занятых дескрипторов в принципе не должно быть).
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Mar 14 2016, 10:23
Сообщение #10


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(cyrax0 @ Mar 14 2016, 10:13) *
Верно, в low_level_output, которая вызывает ETH_Prepare_Transmit_Descriptors, тоже есть проверка на занятость текущего дескриптора

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

static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
static xSemaphoreHandle xTxSemaphore = NULL;
struct pbuf *q;
u8 *buffer ;
__IO ETH_DMADESCTypeDef *DmaTxDesc;
uint16_t framelength = 0;
uint32_t bufferoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t payloadoffset = 0;

if (xTxSemaphore == NULL)
{
vSemaphoreCreateBinary (xTxSemaphore);
}

if (xSemaphoreTake(xTxSemaphore, netifGUARD_BLOCK_TIME))
{
DmaTxDesc = DMATxDescToSet;
buffer = (u8 *)(DmaTxDesc->Buffer1Addr);
bufferoffset = 0;

for(q = p; q != NULL; q = q->next)
{
if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET)
{
goto error;
}

/* Get bytes in current lwIP buffer */
byteslefttocopy = q->len;
payloadoffset = 0;

/* Check if the length of data to copy is bigger than Tx buffer size*/
while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE )
{
/* Copy data to Tx buffer*/
memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) );

/* Point to next descriptor */
DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);

/* Check if the buffer is available */
if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET)
{
goto error;
}

buffer = (u8 *)(DmaTxDesc->Buffer1Addr);

byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}

/* Copy the remaining bytes */
memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), byteslefttocopy );
bufferoffset = bufferoffset + byteslefttocopy;
framelength = framelength + byteslefttocopy;
}

/* Prepare transmit descriptors to give to DMA*/
ETH_Prepare_Transmit_Descriptors(framelength);

/* Give semaphore and exit */
error:

xSemaphoreGive(xTxSemaphore);
}

return ERR_OK;
}
Go to the top of the page
 
+Quote Post
cyrax0
сообщение Mar 14 2016, 13:00
Сообщение #11


Участник
*

Группа: Участник
Сообщений: 22
Регистрация: 9-12-14
Пользователь №: 84 046



У меня то же самое, только без семафоров (что плохо, если я верно понимаю). Не заметил, что в while проверка на занятость очередного дескриптора есть. Соответственно, если любой из них OWN, то занятый буфер мы не перезапишем, в ETH_Prepare_Transmit_Descriptors не попадем и DMA весь пакет не отдадим. Тот кусок, который low_level_output перезапишет, никуда не пойдет, а глобальный указатель на текущий дескриптор останется тем же. Получается, ETH_Prepare_Transmit_Descriptors написана корректно, только условие на проверку одного дескриптора в ней лишнее и путающее - в low_level_output все необходимые дескрипторы уже проверены.
Go to the top of the page
 
+Quote Post
cyrax0
сообщение Mar 21 2016, 14:40
Сообщение #12


Участник
*

Группа: Участник
Сообщений: 22
Регистрация: 9-12-14
Пользователь №: 84 046



Посмотрите, пожалуйста, возможно ли в используемой мной реализации LwIP/драйвера использование low_level_output в разных прерываниях? Или нужен "мьютекс" с выходом из функции и, соответственно, потерей пакета?

CODE

static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
err_t errval;
struct pbuf *q;
u8 *buffer = (u8 *)(DMATxDescToSet->Buffer1Addr);
__IO ETH_DMADESCTypeDef *DmaTxDesc;
uint16_t framelength = 0;
uint32_t bufferoffset = 0;
uint32_t byteslefttocopy = 0;
uint32_t payloadoffset = 0;

DmaTxDesc = DMATxDescToSet;
bufferoffset = 0;

/* copy frame from pbufs to driver buffers */
for(q = p; q != NULL; q = q->next)
{
/* Is this buffer available? If not, goto error */
if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET)
{
errval = ERR_BUF;
goto error;
}

/* Get bytes in current lwIP buffer */
byteslefttocopy = q->len;
payloadoffset = 0;

/* Check if the length of data to copy is bigger than Tx buffer size*/
while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE )
{
/* Copy data to Tx buffer*/
memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) );

/* Point to next descriptor */
DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);

/* Check if the buffer is available */
if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET)
{
errval = ERR_USE;
goto error;
}

buffer = (u8 *)(DmaTxDesc->Buffer1Addr);

byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}

/* Copy the remaining bytes */
memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), byteslefttocopy );
bufferoffset = bufferoffset + byteslefttocopy;
framelength = framelength + byteslefttocopy;
}

/* Note: padding and CRC for transmitted frame
are automatically inserted by DMA */

/* Prepare transmit descriptors to give to DMA*/
ETH_Prepare_Transmit_Descriptors(framelength);

errval = ERR_OK;

error:

/* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
if ((ETH->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET)
{
/* Clear TUS ETHERNET DMA flag */
ETH->DMASR = ETH_DMASR_TUS;

/* Resume DMA transmission*/
ETH->DMATPDR = 0;
}
return errval;
}


Тот же вопрос у меня к функциям lwip (типа udp_send). Разрешено ли их вызывать из разных прерываний, если поставить мьютекс в low_level_output?
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 24th June 2025 - 23:52
Рейтинг@Mail.ru


Страница сгенерированна за 0.01409 секунд с 7
ELECTRONIX ©2004-2016