Implementing prvEMACDeferredInterruptHandlerTask

Hello, I’m trying to pass received data to the TCP/IP in a deferred interrupt handler task. I’ve implemented an ISR called xEthernetHandler as described in xTimerPendFunctionCallFromISR web page: void xEthernetHandler( void ) { BaseType_t xHigherPriorityTaskWoken = pdFALSE;
NVIC_ClearPendingIRQ(ETHERNET_IRQn);

if(smsc9220_RxStatusFifoLevelIrq())
{
    xTimerPendFunctionCallFromISR( prvEMACDeferredInterruptHandlerTask,
                                   NULL,
                                   1,
                                   &xHigherPriorityTaskWoken );

    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
} void prvEMACDeferredInterruptHandlerTask( void * pvParameter1, uint32t ulParameter2 ) { NetworkBufferDescriptort *pxBufferDescriptor; size_t xBytesReceived; unsigned int index = 0;
/* Used to indicate that xSendEventStructToIPTask() is being called because
of an Ethernet receive event. */
IPStackEvent_t xRxEvent;

for( ;; )
{
    /* Wait for the Ethernet MAC interrupt to indicate that another packet
    has been received.  The task notification is used in a similar way to a
    counting semaphore to count Rx events, but is a lot more efficient than
    a semaphore. */
    ulTaskNotifyTake( pdFALSE, portMAX_DELAY );

    /* See how much data was received.  Here it is assumed ReceiveSize() is
    a peripheral driver function that returns the number of bytes in the
    received Ethernet frame. */
    xBytesReceived = smsc9220_recv_size();

    if( xBytesReceived > 0 )
    {
        /* Allocate a network buffer descriptor that points to a buffer
        large enough to hold the received frame.  As this is the simple
        rather than efficient example the received data will just be copied
        into this buffer. */
        pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 );

        if( pxBufferDescriptor != NULL )
        {
            if(smsc9220_recv_packet((unsigned int *)pxBufferDescriptor->pucEthernetBuffer, &index))
            {
                printf("Packet receive failed.n");
                vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
            }
            else
            {
                pxBufferDescriptor->xDataLength = xBytesReceived;

                /* See if the data contained in the received Ethernet frame needs
                to be processed.  NOTE! It is preferable to do this in
                the interrupt service routine itself, which would remove the need
                to unblock this task for packets that don't need processing. */
                if( eConsiderFrameForProcessing( pxBufferDescriptor->pucEthernetBuffer )
                                                                      == eProcessBuffer )
                {
                    /* The event about to be sent to the TCP/IP is an Rx event. */
                    xRxEvent.eEventType = eNetworkRxEvent;

                    /* pvData is used to point to the network buffer descriptor that
                    now references the received data. */
                    xRxEvent.pvData = ( void * ) pxBufferDescriptor;

                    /* Send the data to the TCP/IP stack. */
                    if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )
                    {
                        /* The buffer could not be sent to the IP task so the buffer
                        must be released. */
                        vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );

                        /* Make a call to the standard trace macro to log the
                        occurrence. */
                        iptraceETHERNET_RX_EVENT_LOST();
                    }
                    else
                    {
                        /* The message was successfully sent to the TCP/IP stack.
                        Call the standard trace macro to log the occurrence. */
                        iptraceNETWORK_INTERFACE_RECEIVE();
                    }
                }
                else
                {
                    /* The Ethernet frame can be dropped, but the Ethernet buffer
                    must be released. */
                    vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
                }
            }
        }
        else
        {
            iptraceETHERNET_RX_EVENT_LOST();
        }
    }
}
} I’m using BufferAllocation2.c. ipconfigIPTASKPRIORITY is set to (configMAXPRIORITIES – 1). Two questions: 1. I don’t understand why ulTaskNotifyTake() is necessary, as we already got an Ethernet MAC interrupt (that’s the reason we entered handllerTask function). 2.Althugh xTimerPendFunctionCallFromISR changes xHigherPriorityTaskWoken to be pdTRUE, prvEMACDeferredInterruptHandlerTask is not being called and all task seem to be stuck. Thank you for your help

Implementing prvEMACDeferredInterruptHandlerTask

The library FreeRTOS+TCP does not make use of FreeRTOS’ timer functions. In all published versions of NetworkInterface.c, the TaskNotify mechanism is used to wake-up the prvEMACDeferredInterruptHandlerTask. ~~~~ void xEthernetHandler( void ) { BaseType_t xHigherPriorityTaskWoken = pdFALSE;
NVIC_ClearPendingIRQ(ETHERNET_IRQn);

if(smsc9220_RxStatusFifoLevelIrq())
{
    TaskNotifyGiveFromISR( xDeferredInterruptTaskHandle, ( BaseType_t * ) &xHigherPriorityTaskWoken );
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
} ~~~~
  1. I don’t understand why ulTaskNotifyTake() is necessary
The function will go sleeping until an interrupt has occurred, which is soon after TaskNotifyGiveFromISR(). Note the for(;;) loop. Normally I would let it sleep for a limited amount of time: the PHY’s Link Status (LS) must be checked. When the LS goes low, transmission of packets is impossible and should be inhibited.
2.Although xTimerPendFunctionCallFromISR changes xHigherPriorityTaskWoken to be pdTRUE, prvEMACDeferredInterruptHandlerTask is not being called and all task seem to be stuck.
I’m not sure what the reason is for that, but I would use the ‘lighter’ method of TaskNotify. Regards.

Implementing prvEMACDeferredInterruptHandlerTask

Hi Hein, Thank you for your answer.
  1. But the Ethernet interrupt has already occurred. That’s the reason you’ve entered xEthernetHandler. At that point I would like to read the received bytes but ulTaskNotifyTake() forces me to wait for additional interrupt. Shouldn’t it be at the end of the loop?
I have two additional questions: 2. Shall I disable & clean the interrupt at the begging of xEthernetHandler and enable it somewhere? 3. Is this task running from scheduler startup or only from the first time Ethernet interrupt occures? What happens after receiving the second interrupt? Thank you, Orit

Implementing prvEMACDeferredInterruptHandlerTask

Hi Orit,
But the Ethernet interrupt has already occurred. That’s the reason you’ve entered xEthernetHandler. At that point I would like to read the received bytes but ulTaskNotifyTake() forces me to wait for additional interrupt. Shouldn’t it be at the end of the loop?
It doesn’t matter if the ulTaskNotifyTake() is placed at the beginning or at the end. The function will be sleeping 99.9% of the time. The following will be the order of events, it all happens within micro seconds: ● The interrupt handler xEthernetHandler() is called ● It will call TaskNotifyGiveFromISR() to make prvEMACDeferredInterruptHandlerTask() runnable again ● xHigherPriorityTaskWoken will be pdTRUE, and the YIELD will succeed ● The task running prvEMACDeferredInterruptHandlerTask() is unblocked immediately ( unless there are other runnable tasks with a higher priority ).
  1. Shall I disable & clean the interrupt at the beginning of xEthernetHandler and enable it somewhere?
I would think that it doesn’t make any difference.
  1. Is this task running from scheduler startup or only from the first time Ethernet interrupt occures? What happens after receiving the second interrupt?
Please have a good look at any of the other versions of NetworkInterface.c: you will see that prvEMACDeferredInterruptHandlerTask() will be started up once and that will will run for ever.

Implementing prvEMACDeferredInterruptHandlerTask

Hi Hein, I changed the xTimerPendFunctionCallFromISR() to TaskNotifyGiveFromISR() in the irq and rewrote xNetworkInterfaceInitialise so now it creates the deferred interrupt handler task. I’ve encountered a different problem. Context switch occurs but I keep receiving interrupts that preventing me from continuing. TaskHandle_t xDeferredInterruptTaskHandle = NULL; portBASETYPE xNetworkInterfaceInitialise(void) { portBASETYPE xreturn; xreturn = xTaskCreate( xDeferredInterruptHandlerTask, “RCV”, configMINIMALSTACKSIZE * 3, NULL, mainDEFERREDIRQTASK_PRIORITY, &xDeferredInterruptTaskHandle );
Common_EnableIrq(ETHERNET_IRQn, 223);
return xreturn;
} void xEthernetHandler( void ) { BaseType_t xHigherPriorityTaskWoken = pdFALSE;
Common_DisableIrq(ETHERNET_IRQn);
NVIC_ClearPendingIRQ(ETHERNET_IRQn);

if(smsc9220_RxStatusFifoLevelIrq())
{
    vTaskNotifyGiveFromISR( xDeferredInterruptTaskHandle, ( BaseType_t * ) &xHigherPriorityTaskWoken );
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

Common_EnableIrq(ETHERNET_IRQn, configMAX_SYSCALL_INTERRUPT_PRIORITY);
} What shall be the ETHERNETIRQn irq priority if configKERNELINTERRUPTPRIORITY equals to 255 and configMAXSYSCALLINTERRUPTPRIORITY equals to 191. Thank you, Orit

Implementing prvEMACDeferredInterruptHandlerTask

Interrupt priorities on Cortex-M cores is often the cause of some confusion, so we have tried to explain it on the following page: http://www.freertos.org/RTOS-Cortex-M3-M4.html

Implementing prvEMACDeferredInterruptHandlerTask

Hi Orit, I would be curious to see smsc9220_RxStatusFifoLevelIrq(). In general: there are many possible triggers for ETHERNET_IRQn. Only the triggers that you need (RX, error?) should be enabled (the others are said to be “masked off”). Now if your interrupt-handler is called, the cause of the interrupt must be reset / cleared. How it is to be cleared depends on the peripheral. In some cases it suffices to read the STATUS bits, sometimes a logical 0 or 1 must be written to the status-bit in order to reset it. Yet in other cases, nothing has to be done. So if you observe that the interrupt keeps on being called, try to make clear what the reason is and how it can be cleared. I do not think it is necessary to call Common_DisableIrq() and Common_EnableIrq() from within the interrupt handler.

Implementing prvEMACDeferredInterruptHandlerTask

Hi Hein, The Trigger I need is “RX FIFO Status Interrupt Level”. smsc9220_RxStatusFifoLevelIrq() simply checks the “RX FIFO Status Interrupt Level” bit in interrupt status register: int smsc9220RxStatusFifoLevelIrq(void) { return CHECKBIT(SMSC9220->INT_STS, 3); } I would like the task function to read received bytes everytime interrupt occurs. I do not want the interrupt to occur during the reading process so I disable it, and enable it after reading a packet. The problem is that the interrupt keeps on being called because I receive a lot of packets, and no other task can run. Is it better to implement this listener as a simple reading function (without a loop) and call xTimerPendFunctionCallFromISR() instead of a task?

Implementing prvEMACDeferredInterruptHandlerTask

Hi Orit, ~~~~ int smsc9220RxStatusFifoLevelIrq(void) { /* HT : if CHECKBIT() is only testing the value of a bit, this action will probably not reset the cause of the interrupt. It is documented as a R/WC bit: Read/Write Clear: A register bit with this attribute can be read and written. However, a write of a 1 clears (sets to 0) the corresponding bit and a write of a 0 has no effect */ return CHECK_BIT(SMSC9220->INT_STS, 3); } ~~~~
I would like the task function to read received bytes every time interrupt occurs. I do not want the interrupt to occur during the reading process so I disable it, and enable it after reading a packet.
You better leave the interrupt enabled all the time, no problem. All it does is make your task runnable. The next call to ulTaskNotifyTake() will succeed immediately without blocking. If you disable the RX interrupt, you might miss data or receive them too late.
The problem is that the interrupt keeps on being called because I receive a lot of packets, and no other task can run.
I dare to doubt this theory 🙂 Please check which interrupt bits are enabled in INT_EN and make sure that during the interrupt, the corresponding bits in INT_STS will be cleared. That will stop the heavy load of interrupts. If you still see too many RX data, make sure that the MAC filtering is OK: do not use promiscuous mode but set certain MAC addresses that are used by your device.
Is it better to implement this listener as a simple reading function (without a loop) and call xTimerPendFunctionCallFromISR() instead of a task?
I don’t think so. The proposal is as follows: ~~~~ for( ;; ) { ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); for( ;; ) { xBytesReceived = smsc9220recvsize(); if( xBytesReceived <= 0 ) { break; } /* send the received packet to the IP-task. */ } } ~~~~ The ETH interrupt remains active (enabled) all the time. If an interrupt occurs while reading RX packets, that is no problem. Only vTaskNotifyGiveFromISR() will be called, causing the next ulTaskNotifyTake() to “fall through”. Please have a look of one of the other ports, such as:
FreeRTOS-Plus-TCP/portable/NetworkInterface/ATSAM4E/NetworkInterface.c
Its interrupt handlers will also set a (volatile) bit to indicate what type of interrupt has occurred. This is tested later in the task.

Implementing prvEMACDeferredInterruptHandlerTask

First of all, thank you very much for your time and efforts!
  1. I’ve check INTEN. only RX Fifo level interrupt is enabled (INTEN = 0b1000).
  2. I tried to clean the RX interrupt by setting the third bit in INT_STS. For some reason it remains 1. I even tried to clear all interrupts but it didn’t help. What could be the reason for that?
  3. In order to set the perfect filtering mode, the following bits in MACCR were cleared at smsc initialization: MCPAS (19), PRMS (18), INVFILT (17), HO (15), HPFILT (13). MACCR = 0x1000000C.
  4. In two cases I stop receiving too many RX data: When setting BCAST in MACCR or when setting RX level to FF (i.e. setting FIFOINT to 0xFFFFFFFF).
  5. I’ve checked MACADDRH and MACADDRL and they seem to be correct. If you have additional ideas of what could be wrong, I’ll be very glad to hear.

Implementing prvEMACDeferredInterruptHandlerTask

Hi Orit,
I’ve check INTEN. only RX Fifo level interrupt is enabled (INTEN = 0x1000).
Setting 0x1000 would set bit 12, which is marked as reserved and RO. Did you mean INT_EN = b'00001000' or 0x08 ?
I tried to clean the RX interrupt by setting the third bit in INT_STS. For some reason it remains 1. I even tried to clear all interrupts but it didn’t help. What could be the reason for that?
That is unexpected. What happens if within the interrupt you clear INT_EN bit 3, and re-enable the same bit from within the task, after emptying the RX FIFO?
In order to set the perfect filtering mode, the following bits in MACCR were cleared at smsc initialisation: MCPAS (19), PRMS (18), INVFILT (17), HO (15), HPFILT (13). MACCR = 0x10 00 00 0C.
Did you set bit 28 your self? It is described as reserved. I would set bit 23: “Disable Receive Own (RCVOWN)”, if not you will receive your own packets as well. Later on, you might want to set Pass All Multicast (MCPAS), in case you want to enable LLMNR protocol.
In two cases I stop receiving too many RX data: When setting BCAST in MACCR or when setting RX level to FF (i.e. setting FIFOINT to 0xFFFFFFFF).
With 0xFFFFFFFF you would be writing 1’s to reserved bits, which is normally ‘not done’. Wouldn’t you set it to e.g. 0x48000030 ? ‘0x30’ or 48 bytes is assumed to be a minimum frame length. What I can not find is: how do you know when a received frame is complete?
If you have additional ideas of what could be wrong, I’ll be very glad to hear.
BCAST disables broadcast. Can you check the following: run WireShark on your laptop, connected to the same LAN, and see if there is an abundant amount of broadcast messages. These messages have the MAC address ff:ff:ff:ff:ff:ff as a target. UDP broadcast messages can be quite disturbing for small embedded applications. Solution: filter them as soon as possible, preferably in the ‘deferred interrupt handler task’. The function xPortHasUDPSocket() tells whether a UDP port number is bound to a socket.

Implementing prvEMACDeferredInterruptHandlerTask

Hi Hein, 1. Thanks. I ment 0b1000. 2. Apparently this behavior caused by packets accumulation in TX FIFO. I let it run + changed FIFOINT to 0x4800000C ( 12 dwords, 48 bytes, as the length is specify in dwords). 3. I’ve changed MACCR to 0x90000C (disable receive own, disable heartbeat, enable xmit and enable receiver) and I’ve added xPortHasUDPSocket() to task function as you advised. Thank you very much! As you mentioned, interrupt shall occur when a full packet is received. I thought maybe “Magic Packet Detection” would fit but it detects reception of a specific pattern. I continue to investigate…

Implementing prvEMACDeferredInterruptHandlerTask

I found the solution. RX Status level represents the number of words in RX status (not in RX data), so it is actually represents the number of received packets. Changed RX Status level to 0.