Ethernet RX assertion

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Ethernet RX assertion

Jump to solution
4,228 Views
pjanco
Contributor III

Hi,

I have own board design based on EVK-MIMXRT1060. When transferring large data over TCP/IP in direction from my PC to IMXRT board, it very often ends in this assertion and then restart on watchdog.

 

 

LWIP_ASSERT("Buffer returned by ENET_GetRxFrame() doesn't match any RX buffer descriptor",((idx >= 0) && (idx < ENET_RXBUFF_NUM)));

 

 

I am currently using SDK 2.11.0 but I noticed that in changelog for SDK 2.13.0 is this bugfix:

port: Fixed copying of pbuf contents. Previous code was using an incorrect end
condition and could result in the overrun of the destination buffer if more packets were
on the queue.

It is possible that this can fix mine bug. But migration between SDK versions is so hard with NXP.

I prefer to stay with old SDK and just fix this bug.

Can you provide short bugfix of this overrun for SDK 2.11.0 ?

 

Labels (1)
0 Kudos
1 Solution
3,403 Views
AbnerWang
NXP Employee
NXP Employee

Hi Peter,

Great! Although I still can't reproduce this issue on my local environment with your code, maybe result from laptop performance. But I think I can make an conclusion now.

Previously we have GetRxFrameSize before ReadFrame to check Rx frame status for non-zero-copy case. There's the code snippet in GetRxFrameSize:

/* Add check for abnormal case. */
if (curBuffDescrip->length == 0U)
{
isReturn = true;
result = kStatus_ENET_RxFrameError;
break;
}

When adding zero-copy function, I didn't use this logic because I never meet this corner case and consider it a redundant operation. And this IP is very old, I can't find software/IP designer to review the history. Now I will think this is an ENET IP issue, so also add this check for GetRxFrame. Meanwhile I improve the code logic to check all EMPTY flag valid in case other corner case occurs and driver returns success. Attachment is the function with fix. Can you try and tell me the result?

Best Regards.
Abner

View solution in original post

23 Replies
2,209 Views
victorrsoliveira
Contributor I

Hey @AbnerWang

I am updating this thread just to inform that I am having a similar issue. I just want to know if this issue has been solved in any version of the SDK. I am using version 2.13.0, but I've looked newest versions of the SDK and this piece of code looks the same. 

Can you confirm that?

I haven't tried your solution code already, but I will try it later.

Thanks!

0 Kudos
2,181 Views
AbnerWang
NXP Employee
NXP Employee

Did you check the 2.14.0 SDK? This should be included in it.

Best Regards.
Abner
0 Kudos
4,016 Views
danielchen
NXP TechSupport
NXP TechSupport

Hi

I would suggest you compare middleware\lwip\port\enet_ethernetif_kinetis.c,   

 

Regards

Daniel

0 Kudos
3,960 Views
pjanco
Contributor III

Hi,

I am able to reproduce problem on latest SDK 2.13 on board MIMXER1060-EVK. So, problem is not fixed on latest SDK.

I foud exact line where memory overflow happened. It is file fsl_enet.c on line 2508:

 

 

index = 0;
do
{
 newBuff = (uintptr_t)(uint8_t *)handle->rxBuffAlloc(base, handle->userData, ringId);
 if (newBuff != 0U)
 {
   assert((uint64_t)newBuff + handle->rxBuffSizeAlign[ringId] - 1U <= UINT32_MAX);
   rxBuffer = &rxFrame->rxBuffArray[index];

 

 

For some reason, end condition of do-while loop is not triggered and index value is incremented above size of rxBuffArray, which is 1. Simply, if index is bigger than 0, is it overflowed.

It is happened when there is a lot of packets on input side and processor is too busy at the time to handle them. In my application, it is happened during OTA upgrade when I am writing received data to Hyperflash. But conditions can be simulated by taskENTER_CRITICAL macro and some delay after.

I made a "dirty" FIX like this:

 

 

index = 0;
do
{
 if(0U == (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
  newBuff = NULL;
 else
  newBuff = handle->rxBuffAlloc(base, handle->userData, ringId);

 if (newBuff != NULL)
 {
  rxBuffer = &rxFrame->rxBuffArray[index];

 

 

I am sure this not universal way how to fix it. It is working only on this exact configuration.

Someone responsible for this driver should really look at it.

 

Regards,

Peter.

0 Kudos
3,738 Views
AbnerWang
NXP Employee
NXP Employee

Hi Peter

Another possible issue is when Rx BD E(Empty) bit is 0 which means this BD has been updated but L(Last) bit in this BD is not set 1 in one BD stores one frame situation. For one frame in multiple BDs case, current code also has risk to enter buffer allocation operation before entire frame input.

Can you try this fast fix: Define status_t result = kStatus_ENET_RxFrameEmpty instead of kStatus_Success as initial value at the start of function ENET_GetRxFrame().

And tell me which setting you use, received complete frame is always in one BD or it may be in multiple BDs according to actual received Rx length. This fast fix should fix both if my thought is right. But L(Last)=0 when E(Empty)=0 in one frame one BD case is beyond our previous understanding and I need to double check this with design team.

Best Regards.
Abner
0 Kudos
3,727 Views
pjanco
Contributor III

Hi,
my project was based on "evkbmimxrt1060_lwip_httpssrv_mbedTLS_freertos" and I did not change low level enet configuration.


I can prepare testing project for you to reproduce the problem on MIMX board. Goal is to create long critical section and then send a lot of packets. Some days, I am not able to reproduce this problem and next day, it happed very often.

FIX result = kStatus_ENET_RxFrameEmpty did not solve the problem.

1.I did not change this. It have same value as in SDK example.
buffCfg[0].rxBdNumber = 5
buffCfg[0].rxBuffSizeAlign = 1536

2. did not change this. It have same value as in SDK example.
MAX_BUFFERS_PER_FRAME = 1

#define ENET_FRAME_MAX_FRAMELEN 1518U

#define ENET_RXBUFF_SIZE (ENET_FRAME_MAX_FRAMELEN)

#define MAX_BUFFERS_PER_FRAME \
((ENET_FRAME_MAX_FRAMELEN / ENET_RXBUFF_SIZE) + ((ENET_FRAME_MAX_FRAMELEN % ENET_RXBUFF_SIZE == 0) ? 0 : 1))

 

Regards,

Peter

0 Kudos
3,725 Views
AbnerWang
NXP Employee
NXP Employee

Hi Peter,

Thanks for these information. Can you do a research?

1. Does it enter second loop 'rxBuffer = &rxFrame->rxBuffArray[index]'with index=1 due to isLastBuff=false. If so, it means (0U == (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK)).

2. Check the code before comment /* Drop the error frame. */. Run ENET_GetRxFrameErr() then break or run result = kStatus_ENET_RxFrameEmpty; then break. Or break due to (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK)). Which of these three flow code runs when the issue occurs?

 

Best Regards.
Abner
0 Kudos
3,715 Views
pjanco
Contributor III

Hi,

1.

Yes it enter while loop second time with index == 1.

 

2.

I am not sure what you need.

I added tst_var and put break point to "Overflow problem assertion". Value of tst_var on breakpoint is 0.

See code below:

status_t ENET_GetRxFrame(ENET_Type *base, enet_handle_t *handle, enet_rx_frame_struct_t *rxFrame, uint8_t ringId)
{
    assert(handle != NULL);
    assert(ringId < (uint8_t)FSL_FEATURE_ENET_QUEUE);
    assert(handle->rxBdRing[ringId].rxBdBase != NULL);
    assert(rxFrame != NULL);
    assert(rxFrame->rxBuffArray != NULL);

    status_t result                              = kStatus_Success;
    enet_rx_bd_ring_t *rxBdRing                  = &handle->rxBdRing[ringId];
    volatile enet_rx_bd_struct_t *curBuffDescrip = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
    void *newBuff                                = NULL;
    bool isLastBuff                              = false;
    uint16_t buffLen                             = 0;
    enet_buffer_struct_t *rxBuffer;
    uint16_t index;
    uint32_t address;
    void *buffer;

    status_t tst_var = 0xFFFFFFFF;

    /* Check the current buffer descriptor's empty flag. If empty means there is no frame received. */
    if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK))
    {
        result = kStatus_ENET_RxFrameEmpty;
    }
    else
    {
        index = rxBdRing->rxGenIdx;
        do
        {
            /* Find the last buffer descriptor. */
            if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
            {
                /* The last buffer descriptor stores the status of rhis received frame. */
                result = ENET_GetRxFrameErr((enet_rx_bd_struct_t *)(uint32_t)curBuffDescrip, &rxFrame->rxFrameError);
                tst_var = result;
                break;
            }

            /* Can't find the last BD flag, no valid frame. */
            index          = ENET_IncreaseIndex(index, rxBdRing->rxRingLen);
            curBuffDescrip = rxBdRing->rxBdBase + index;
            if (index == rxBdRing->rxGenIdx)
            {
                result = kStatus_ENET_RxFrameEmpty;
                break;
            }
        } while (0U == (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK));
    }

    /* Drop the error frame. */
    if (result == kStatus_ENET_RxFrameError)
    {
        curBuffDescrip = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
        do
        {
            /* The last buffer descriptor of a frame. */
            if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
            {
                isLastBuff = true;
            }

            /* Clears status including the owner flag. */
            curBuffDescrip->control &= ENET_BUFFDESCRIPTOR_RX_WRAP_MASK;
            /* Sets the receive buffer descriptor with the empty flag. */
            curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK;

            /* Increase current buffer descriptor to the next one. */
            rxBdRing->rxGenIdx = ENET_IncreaseIndex(rxBdRing->rxGenIdx, rxBdRing->rxRingLen);
            curBuffDescrip     = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
        } while (!isLastBuff);

        ENET_ActiveReadRing(base, ringId);

        return result;
    }
    else if (result != kStatus_Success)
    {
        return result;
    }
    else
    {
        /* Intentional empty */
    }

    /* Get the valid frame */
    curBuffDescrip = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
    index          = 0;
    do
    {







#if 0//(defined(QB_SDK_CHANGES) && (QB_SDK_CHANGES > 0U))//My fix of problem
    	if(0U == (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
    		newBuff = NULL;
    	else
    		newBuff = handle->rxBuffAlloc(base, handle->userData, ringId);
#else
    	newBuff = handle->rxBuffAlloc(base, handle->userData, ringId);
#endif
        if (newBuff != NULL)
        {
/*
Overflow problem assertion
*/
        	if(index >0)
        	{
        		while(1);
        	}






            rxBuffer = &rxFrame->rxBuffArray[index];

#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET
            address = MEMORY_ConvertMemoryMapAddress((uint32_t)curBuffDescrip->buffer, kMEMORY_DMA2Local);
#else
            address = (uint32_t)curBuffDescrip->buffer;
#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */
#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
            if (handle->rxMaintainEnable[ringId])
            {
                DCACHE_InvalidateByRange(address, handle->rxBuffSizeAlign[ringId]);
            }
#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */

            rxBuffer->buffer = (void *)(uint32_t *)address;

            /* The last buffer descriptor of a frame. */
            if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
            {
                /* This is a valid frame. */
                isLastBuff       = true;
                rxFrame->totLen  = curBuffDescrip->length;
                rxBuffer->length = curBuffDescrip->length - buffLen;

                rxFrame->rxAttribute.promiscuous = false;
                if (0U != (base->RCR & ENET_RCR_PROM_MASK))
                {
                    if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_MISS_MASK))
                    {
                        rxFrame->rxAttribute.promiscuous = true;
                    }
                }
#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE
                rxFrame->rxAttribute.timestamp = curBuffDescrip->timestamp;
#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */
            }
            else
            {
                rxBuffer->length = curBuffDescrip->length;
                buffLen += rxBuffer->length;
            }

            /* Give new buffer from application to BD */

#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET
            buffer =
                (void *)(uint32_t *)MEMORY_ConvertMemoryMapAddress((uint32_t)(uint32_t *)newBuff, kMEMORY_Local2DMA);
#else
            buffer  = newBuff;
#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */
#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
            if (handle->rxMaintainEnable[ringId])
            {
                DCACHE_InvalidateByRange((uint32_t)(uint32_t *)buffer, handle->rxBuffSizeAlign[ringId]);
            }
#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */

            curBuffDescrip->buffer = buffer;

            /* Clears status including the owner flag. */
            curBuffDescrip->control &= ENET_BUFFDESCRIPTOR_RX_WRAP_MASK;
            /* Sets the receive buffer descriptor with the empty flag. */
            curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK;

            /* Increase Rx array index and the buffer descriptor address. */
            index++;
            rxBdRing->rxGenIdx = ENET_IncreaseIndex(rxBdRing->rxGenIdx, rxBdRing->rxRingLen);
            curBuffDescrip     = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
        }
        else
        {
            /* Drop frame if there's no new buffer memory */

            /* Free the incomplete frame buffers. */
            while (index-- != 0U)
            {
                handle->rxBuffFree(base, &rxFrame->rxBuffArray[index].buffer, handle->userData, ringId);
            }

            /* Update left buffers as ready for next coming frame */
            do
            {
                /* The last buffer descriptor of a frame. */
                if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
                {
                    isLastBuff = true;
                }

                /* Clears status including the owner flag. */
                curBuffDescrip->control &= ENET_BUFFDESCRIPTOR_RX_WRAP_MASK;
                /* Sets the receive buffer descriptor with the empty flag. */
                curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK;

                /* Increase current buffer descriptor to the next one. */
                rxBdRing->rxGenIdx = ENET_IncreaseIndex(rxBdRing->rxGenIdx, rxBdRing->rxRingLen);
                curBuffDescrip     = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
            } while (!isLastBuff);

            result = kStatus_ENET_RxFrameDrop;
            break;
        }
    } while (!isLastBuff);

    ENET_ActiveReadRing(base, ringId);

    return result;
}

 

 

0 Kudos
3,707 Views
AbnerWang
NXP Employee
NXP Employee

Hi Peter,

According to your test, obviously it enters (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK)) at start of function, here soft logic is checking the complete frame data and BD status are all ready. But it doesn't hit again at next time which I mention in point 1: 'Does it enter second loop ......... If so, it means (0U == (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))'. As software design, these two curBuffDescrip should be same value, can you confirm is the curBuffDescrip changing in these two place? If not change, why this RX_LAST bit becomes from 1 to 0, that's what I get confused.

Of course you give me a SDK package which can reproduce this issue and tell me the test environment is great. Previously I just used the lwip iperf/ping bm cases, then PC used Ostinato to send high load frames with almost 90Mbps+ throughput, can't reproduce this issue.

Best Regards.
Abner
0 Kudos
3,754 Views
AbnerWang
NXP Employee
NXP Employee

Hello Peter,
I am checking this problem now. Do you have any way to reproduce this problem on RT1060 board with raw SDK code? Or any code change to make it occur easily and I can try it on the board. BTW, what is Rx buffer length setting you there, does each BD buffer store one complete frame?

'For some reason, the end condition of the do-while loop is not triggered and the index value is incremented over the size of rxBuffArray, which is 1. Simply, if the index is greater than 0, it has overflowed.'
From this word, I doubt that you use the BD Rx buffer length (for example 500Bytes) < Max Rx frame length in this network (for example 1518Bytes). In this situation, rxFrame->rxBuffArray should be provided by user and the array length should be 4 to store 500+500+500+18 frame fragment's addresses from driver to rxFrame->rxBuffArray[0~3].buffer.
Could you confirm:
1. What is this setting? Did you customize it?
buffCfg[x].rxBuffSizeAlign = sizeof(rx_buffer_t); /* Aligned receive data buffer size. */
2. When resizing the Rx buffer for a single BD, make sure that the 'buffers' below has a sufficient array length to get all buffers of a complete frame.
enet_buffer_struct_t buffers[MAX_BUFFERS_PER_FRAME];
enet_rx_frame_struct_t rxFrame = {.rxBuffArray = &buffers[0]};
ENET_GetRxFrame(ethernetif->base, &ethernetif->handle, &rxFrame, 0);

Best Regards.
Abner
0 Kudos
3,641 Views
AbnerWang
NXP Employee
NXP Employee

Hello @pjanco ,

Any update on this topic?

Best Regards.
Abner
0 Kudos
3,636 Views
pjanco
Contributor III

Hi,

I will prepare demo project that can reproduce the problem soon but I am busy right now with some other work.

 

I also realize that my IMXRT-1062EVK kit have soldered Hyperflash chip instead of NORflash and you can not just run my project on your side without changes. Is that a problem?

I will post here demo project in few days.

 

Regards,

Peter

0 Kudos
3,630 Views
AbnerWang
NXP Employee
NXP Employee

Hi Peter,

That may be a problem to find a hyperflash version board my here. Whatever you can give me the project to check.

But according to your feedback I think we have been close to the root cause. Please debug your project as I mentioned 'As software design, these two curBuffDescrip should be same value, can you confirm is the curBuffDescrip changing in these two place? If not change, why this RX_LAST bit becomes from 1 to 0, that's what I get confused.'. Figure out either software or hardware modify the 'curBuffDescrip' structure address or 'RX_LAST' bit. Maybe we can fix it without your package.

Best Regards.
Abner
0 Kudos
3,611 Views
pjanco
Contributor III

Hi,

there is my receive function:

status_t ENET_GetRxFrame(ENET_Type *base, enet_handle_t *handle, enet_rx_frame_struct_t *rxFrame, uint8_t ringId)
{
    assert(handle != NULL);
    assert(ringId < (uint8_t)FSL_FEATURE_ENET_QUEUE);
    assert(handle->rxBdRing[ringId].rxBdBase != NULL);
    assert(rxFrame != NULL);
    assert(rxFrame->rxBuffArray != NULL);

    status_t result                              = kStatus_Success;
    enet_rx_bd_ring_t *rxBdRing                  = &handle->rxBdRing[ringId];
    volatile enet_rx_bd_struct_t *curBuffDescrip = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
    bool isLastBuff                              = false;
    uintptr_t newBuff                            = 0;
    uint16_t buffLen                             = 0;
    enet_buffer_struct_t *rxBuffer;
    uintptr_t address;
    uintptr_t buffer;
    uint16_t index;

    uint16_t control2 = 0;
    volatile enet_rx_bd_struct_t *curBuffDescrip2 = 0;
    enet_rx_bd_ring_t *rxBdRing2 = 0;
	uint16_t rxGenIdx2 = 0;
	volatile enet_rx_bd_struct_t *rxBdBase2 = 0;

    /* Check the current buffer descriptor's empty flag. If empty means there is no frame received. */
    if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK))
    {
        result = kStatus_ENET_RxFrameEmpty;
    }
    else
    {
        index = rxBdRing->rxGenIdx;
        do
        {
            /* Find the last buffer descriptor. */
            if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
            {
            	curBuffDescrip2 = curBuffDescrip;
            	control2 = curBuffDescrip->control;
            	rxBdRing2 = rxBdRing;
            	rxGenIdx2 = rxBdRing->rxGenIdx;
            	rxBdBase2 = rxBdRing->rxBdBase;
                /* The last buffer descriptor stores the status of rhis received frame. */
                result = ENET_GetRxFrameErr((enet_rx_bd_struct_t *)(uint32_t)curBuffDescrip, &rxFrame->rxFrameError);
                break;
            }

            /* Can't find the last BD flag, no valid frame. */
            index          = ENET_IncreaseIndex(index, rxBdRing->rxRingLen);
            curBuffDescrip = rxBdRing->rxBdBase + index;
            if (index == rxBdRing->rxGenIdx)
            {
                result = kStatus_ENET_RxFrameEmpty;
                break;
            }
        } while (0U == (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK));
    }

    /* Drop the error frame. */
    if (result == kStatus_ENET_RxFrameError)
    {
        curBuffDescrip = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
        do
        {
            /* The last buffer descriptor of a frame. */
            if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
            {
                isLastBuff = true;
            }

            /* Clears status including the owner flag. */
            curBuffDescrip->control &= ENET_BUFFDESCRIPTOR_RX_WRAP_MASK;
            /* Sets the receive buffer descriptor with the empty flag. */
            curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK;

            /* Increase current buffer descriptor to the next one. */
            rxBdRing->rxGenIdx = ENET_IncreaseIndex(rxBdRing->rxGenIdx, rxBdRing->rxRingLen);
            curBuffDescrip     = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
        } while (!isLastBuff);

        ENET_ActiveReadRing(base, ringId);

        return result;
    }
    else if (result != kStatus_Success)
    {
        return result;
    }
    else
    {
        /* Intentional empty */
    }

    /* Get the valid frame */
    curBuffDescrip = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
    index          = 0;

    if(0U == (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
    {
    	printf("%d %d %d %d %d", control2, curBuffDescrip2, rxBdRing2, rxGenIdx2, rxBdBase2);
    	while(1);
    }

    do
    {
        newBuff = (uintptr_t)(uint8_t *)handle->rxBuffAlloc(base, handle->userData, ringId);

        if (index > 0)
			__NOP();

        if (newBuff != 0U)
        {
			if (index > 0)
			{
				__NOP();
				while(1);
			}

            assert((uint64_t)newBuff + handle->rxBuffSizeAlign[ringId] - 1U <= UINT32_MAX);
            rxBuffer = &rxFrame->rxBuffArray[index];

#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET
            address = MEMORY_ConvertMemoryMapAddress(curBuffDescrip->buffer, kMEMORY_DMA2Local);
#else
            address = curBuffDescrip->buffer;
#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */
#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
            if (handle->rxMaintainEnable[ringId])
            {
                DCACHE_InvalidateByRange(address, handle->rxBuffSizeAlign[ringId]);
            }
#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */

            rxBuffer->buffer = (void *)(uint8_t *)address;

            /* The last buffer descriptor of a frame. */
            if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
            {
                /* This is a valid frame. */
                isLastBuff       = true;
                rxFrame->totLen  = curBuffDescrip->length;
                rxBuffer->length = curBuffDescrip->length - buffLen;

                rxFrame->rxAttribute.promiscuous = false;
                if (0U != (base->RCR & ENET_RCR_PROM_MASK))
                {
                    if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_MISS_MASK))
                    {
                        rxFrame->rxAttribute.promiscuous = true;
                    }
                }
#ifdef ENET_ENHANCEDBUFFERDESCRIPTOR_MODE
                rxFrame->rxAttribute.timestamp = curBuffDescrip->timestamp;
#endif /* ENET_ENHANCEDBUFFERDESCRIPTOR_MODE */
            }
            else
            {
                rxBuffer->length = curBuffDescrip->length;
                buffLen += rxBuffer->length;
            }

            /* Give new buffer from application to BD */

#if defined(FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET) && FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET
            buffer = MEMORY_ConvertMemoryMapAddress(newBuff, kMEMORY_Local2DMA);
#else
            buffer  = newBuff;
#endif /* FSL_FEATURE_MEMORY_HAS_ADDRESS_OFFSET */
#if defined(FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
            if (handle->rxMaintainEnable[ringId])
            {
                DCACHE_InvalidateByRange(buffer, handle->rxBuffSizeAlign[ringId]);
            }
#endif /* FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */

            curBuffDescrip->buffer = (uint32_t)buffer;

            /* Clears status including the owner flag. */
            curBuffDescrip->control &= ENET_BUFFDESCRIPTOR_RX_WRAP_MASK;
            /* Sets the receive buffer descriptor with the empty flag. */
            curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK;

            /* Increase Rx array index and the buffer descriptor address. */
            index++;
            rxBdRing->rxGenIdx = ENET_IncreaseIndex(rxBdRing->rxGenIdx, rxBdRing->rxRingLen);
            curBuffDescrip     = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
        }
        else
        {
            /* Drop frame if there's no new buffer memory */

			if (index > 0)
			{
				__NOP();
				while (1);
			}

            /* Free the incomplete frame buffers. */
            while (index-- != 0U)
            {
                handle->rxBuffFree(base, &rxFrame->rxBuffArray[index].buffer, handle->userData, ringId);
            }

            /* Update left buffers as ready for next coming frame */
            do
            {
                /* The last buffer descriptor of a frame. */
                if (0U != (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
                {
                    isLastBuff = true;
                }

                /* Clears status including the owner flag. */
                curBuffDescrip->control &= ENET_BUFFDESCRIPTOR_RX_WRAP_MASK;
                /* Sets the receive buffer descriptor with the empty flag. */
                curBuffDescrip->control |= ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK;

                /* Increase current buffer descriptor to the next one. */
                rxBdRing->rxGenIdx = ENET_IncreaseIndex(rxBdRing->rxGenIdx, rxBdRing->rxRingLen);
                curBuffDescrip     = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
            } while (!isLastBuff);

            result = kStatus_ENET_RxFrameDrop;
            break;
        }
    } while (!isLastBuff);

    ENET_ActiveReadRing(base, ringId);

    return result;
}

 

 And there are debug variables:

debug.jpg

 

Regards,

Peter.

0 Kudos
3,577 Views
AbnerWang
NXP Employee
NXP Employee

Hi Peter,

Your provided code shows curBuffDescrip2=0x20000090, curBuffDescrip=base+genIdx=0x20000088.

1. I guess current BD LAST bit = 0, next BD LAST bit = 1. So curBuffDescrip++.

Firstly check whether (0U == (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK)) and (0U == (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK)) happen at the same time when function just enters. As I mentioned before, previously I think BD should be set LAST bit when EMPTY sets 1->0 by DMA when one frame one BD case as you are. But now I doubt this, so please help me confirm it. If this is the root cause, I need to wait the feedback from Soc design team.

Another strange thing is the second BD set LAST bit as 1, usually this occurs when one frame stores in two BDs. Every time driver receives frame from BDs, it will clear LAST bit. So I don't know why current BD and next BD's EMPTY bits = 1 but only second BD's LAST bit = 1. Can you confirm whether curBuffDescrip->buffer takes entire frame and curBuffDescrip->length is right. And check (nextBuffDescrip)->buffer/length value together.

2. If above test can't make a conclusion, it looks either stack overflow somewhere modifies the value of curBuffDescrip. Can you debug further to locate the specific code doing this? From raw code logic, I can't find the root cause.

Best Regards.
Abner
0 Kudos
3,535 Views
pjanco
Contributor III

Hi,

I am not sure what you need to see. Maybe you can send code you want to run on my side.

I put this code at the begin of function:

status_t ENET_GetRxFrame(ENET_Type *base, enet_handle_t *handle, enet_rx_frame_struct_t *rxFrame, uint8_t ringId)
{
    assert(handle != NULL);
    assert(ringId < (uint8_t)FSL_FEATURE_ENET_QUEUE);
    assert(handle->rxBdRing[ringId].rxBdBase != NULL);
    assert(rxFrame != NULL);
    assert(rxFrame->rxBuffArray != NULL);

    status_t result                              = kStatus_Success;
    enet_rx_bd_ring_t *rxBdRing                  = &handle->rxBdRing[ringId];
    volatile enet_rx_bd_struct_t *curBuffDescrip = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
    bool isLastBuff                              = false;
    uintptr_t newBuff                            = 0;
    uint16_t buffLen                             = 0;
    enet_buffer_struct_t *rxBuffer;
    uintptr_t address;
    uintptr_t buffer;
    uint16_t index;

    uint16_t control2 = 0;
    volatile enet_rx_bd_struct_t *curBuffDescrip2 = 0;
    enet_rx_bd_ring_t *rxBdRing2 = 0;
	uint16_t rxGenIdx2 = 0;
	volatile enet_rx_bd_struct_t *rxBdBase2 = 0;
	uint8_t condition = 0;

	if (0U == (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK) && 0U == (curBuffDescrip->control & ENET_BUFFDESCRIPTOR_RX_LAST_MASK))
	{
		condition++;
	}

 

And at my error breakpoint, variable condition have value 1:

condition.jpg

Regards,

Peter.

 

 

0 Kudos
3,493 Views
AbnerWang
NXP Employee
NXP Employee

Hi Peter,

Perfect! Can you provide the buffer/length content in current BD and next BD. I want to check what's frame data in BDs at this time. When this issue occurs, print or backup the 'curBuffDescrip->length' of data in curBuffDescrip->buffer and curBuffDescrip->length value, plus same content of nextBuffDescrip->buffer/length.

To get nextBuffDescrip is just moving the index:

rxBdRing->rxGenIdx = ENET_IncreaseIndex(rxBdRing->rxGenIdx, rxBdRing->rxRingLen);
nextBuffDescrip = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;

 

Best Regards.
Abner
0 Kudos
3,284 Views
AbnerWang
NXP Employee
NXP Employee

Hi @pjanco,

Any update of it? We should be very close to the root cause.

Best Regards.
Abner
0 Kudos
3,245 Views
pjanco
Contributor III

Hi,

there is my debug print screen with requested values:

pjanco_1-1681743048140.png

 

I am also sending my whole testing project in attachment. My project is configured to use Hyperflash instead of Norflash. To use project on your side, I recommend to download "evkmimxrt1060_lwip_httpsrv_freertos" from SDK examples and then copy changed files from my project to your project.

Changed files are: "fsl_enet.c", "httpsrv_freertos.c", "httpsrv_fs_data.c"

And in file "lwip_httpsrv_freertos.c" I just changed configured IP address to fit my network.

To reproduce the problem, put breakpoint to "while(1);//STAY THERE", then open index web page on chrome web browser. Load some 40MB big file and press upload file:

pjanco_2-1681743257289.png

Now just wait for the breakpoint.

There is no feedback of upload on the web page. I am using ping to monitoring if board is still alive.

 

Regards,

Peter.

0 Kudos
3,404 Views
AbnerWang
NXP Employee
NXP Employee

Hi Peter,

Great! Although I still can't reproduce this issue on my local environment with your code, maybe result from laptop performance. But I think I can make an conclusion now.

Previously we have GetRxFrameSize before ReadFrame to check Rx frame status for non-zero-copy case. There's the code snippet in GetRxFrameSize:

/* Add check for abnormal case. */
if (curBuffDescrip->length == 0U)
{
isReturn = true;
result = kStatus_ENET_RxFrameError;
break;
}

When adding zero-copy function, I didn't use this logic because I never meet this corner case and consider it a redundant operation. And this IP is very old, I can't find software/IP designer to review the history. Now I will think this is an ENET IP issue, so also add this check for GetRxFrame. Meanwhile I improve the code logic to check all EMPTY flag valid in case other corner case occurs and driver returns success. Attachment is the function with fix. Can you try and tell me the result?

Best Regards.
Abner