Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Table of Contents

Initialization

When µC/TCP-IP is initialized, the Network Device Driver allocates a memory block for all Receive descriptors; this is performed via calls to µC/LIB.

...

The DMA controller is initialized and the hardware is informed of the address of descriptor list.

Panel

...

title

...

Figure -

...

Allocation of buffers

...

Image Added


Panel
bgColor#f0f0f0

(1) The result of Mem_Init() and the first step in the

...

initialization of the Network Device Driver is the allocation of buffers.



Panel

...

title

...

Figure -

...

Descriptor allocation

...

Image Added


Panel
bgColor#f0f0f0

(1)  µC/TCP-IP allocates a list of descriptors based on the network device driver configuration and sets each address field to point to the start address of a receive buffer.



Panel

...

title

...

Figure -

...

Reception descriptor pointers initialization

...

Image Added


Panel
bgColor#f0f0f0

(1) The network device driver initializes three pointers. One to track the current descriptor which is expected to contain the next received frame

...

(2)

...

 A second pointer to remember the descriptor list boundaries.

...

(3)

...

 Finally, the DMA controller is initialized and hardware is informed of the descriptor list starting address.


Reception

Panel

...

title

...

Figure -

...

Receiving an Ethernet frame with DMA

...

Image Added


Panel
bgColor#f0f0f0

(1)

...

 With each new received frame, the network device driver

...

increments BufDescPtrCur

...

 by 1 and wraps around the descriptor list as necessary

...

(2)

...

 The hardware applies the same logic to an internal descriptor pointer.


When a received frame is processed, the driver gets a pointer to a new data buffer and updates the current descriptor address field. The previous buffer address is passed to the protocol stack for processing. If a buffer pointer cannot be obtained, the existing pointer remains in place and the frame is dropped.

ISR

...

Handler

When a frame is received, the DMA controller will generate an interrupt. The ISR handler must signal the network interface. The network interface will automatically call the receive function.

DMA are using use a control data structure that indicates the transfer configuration. These data structure structures are called descriptors and in order to be able to receive multiple packets at the same time, we need multiple descriptors that we arrange in a list.

...

This pointer doesn’t move, it always points to the last descriptor.

Initializing device

...

Reception Descriptors

NetDev_Start() starts the network interface hardware by initializing the receive and transmit descriptors, enabling the transmitter and receiver and starting and enabling the DMA. Initialization the Rx DMA descriptors list can be done in a sub-function NetDev_RxDescInit(). The memory needed by the descriptors must be reserved by the function NetDev_Init(). Initialization of the Rx descriptor list consist consists of setting the descriptors pointers of the NET_DEV_DATA and fill filling all Receive descriptors with a Receive buffer.

...

You also have to initialize each descriptor. You must initialize descriptor field fields according to the controller documentation. Note that the descriptor must be configured to be owned by the DMA and not the software. Here is the pseudo code of the descriptor ring initialization:

 

 

Listing 7-7 Descriptor Ring Initialization

L7-7(1) Initialize the descriptor pointer to the first Rx buffer descriptor of pdev_data.

L7-7(2) Initialize current descriptor pointer of pdev_data to the first Rx buffer descriptor of pdev_data.

L7-7(3) Initialize last descriptor pointer of pdev_data to the last descriptor declared using pointer arithmetic and the Rx descriptor number defined by RxDescNbr in NET_DEV_CFG_ETHER.

L7-7(4) Repeat for each descriptor defined in .RxDescNbr in NET_DEV_CFG_ETHER.

L7-7(5) Initialize the description fields to their initial value as defined by the DMA Descriptor's documentation in the device data sheet. There might be more than a single field to define depending of the specifications of the DMA used (a field describing the size of associated data buffer might be present and require to be initialized to the length of the requested buffer area below.)

L7-7(6) Initialize the status bit of the descriptor to specify that it is owned by the DMA engine (not by the software).

L7-7(7) Call NetBuf_GetDataPtr() to get a buffer area and initialize the descriptor's buffer start address to the address of the buffer area.

L7-7(8) If an error occurred during the allocation of a buffer area, return as it might mean that there is an issue with the values declared in NET_DEV_CFG_ETHER and the available device memory or heap size.

L7-7(9) Initialize the next descriptor location of the current descriptor to the next descriptor using pointer arithmetic.

L7-7(10) Increment the descriptor using pointer arithmetics.

Once the Rx descriptor ring is ready, you have to configure controller register to enable the controller reception. Controller's interrupt generation should be enabled for the following events: reception of a packet with and without errors and completed transmission of a packet with and without errors.

What needs to be done in the ISR for reception

NetDev_ISR_Handler() is the function called by the IF layer when a Ethernet related ISR is generated and handled by the BSP layer. When Rx ISR occur, only NetOS_IF_RxTaskSignal() has to be called. Nothing has to be done on RxBufDescPtrCur. The complete receive process is delayed in order to have the fastest ISR handler as possible. If an error occurred on RX, you can increment driver statistic into the ISR handler or into NetDev_Rx(), it’s up to you to determine which of the cases is best. You must always signal the core that a packet is received using NetOS_IF_RxTaskSignal(). If you fail to notify the core for each packet, a buffer leak will occur and performance will degrade. NetDev_Rx() will discard the packet and it will say to the µC/TCP-IP module that the packet is received with an error.

Moving buffers from the device to the TCP-IP stack using DMA

NetDev_Rx() is called by core once a NetOS_IF_RxTaskSignal() call has been made to recover the received packet. If data received is valid, this function must replace the buffer of the current descriptor with a free buffer. Also, the current descriptor must be restarted (owned by the DMA) to be able to receive again. RxBufDescPtrCur must be moved to point on the next descriptor. The sub-function NetDev_RxDescPtrCurInc() is called to restart the current descriptor and to move the pointer to the next descriptor. If an error has occurred, you have to set data and length pointers to 0 and return an error. If there is no free Rx buffer available, the packet must be discarded by leaving the current data buffer assigned to the DMA, increment the current descriptor and return an error to the µC/TCP-IP module. Here is a pseudo code of NetDev_Rx():

 

 

 

Listing 7-8 Packet Reception

L7-8(1) Obtain pointer to the next ready descriptor.

L7-8(2) If this descriptor is owned by the DMA (e.g., the DMA is currently receiving data or hasn't started receiving data yet). The descriptor has to be owned by the software to be processed. If owned by the DMA, set *perr to NET_DEV_ERR_RX signaling that the interrupt that there was an error within the reception. Set *size to 0, *p_data to (CPU_INT08U*)0 and return.

L7-8(3) If a reception error is reported in the descriptor set *perr to NET_DEV_ERR_RX notifying that an error occured within the reception. Set *size to 0, *p_data to (CPU_INT08U*)0 and return.

L7-8(4) If the frame length is either runt or overrun set *perr to NET_DEV_ERR_INVALID_SIZE signaling that the size of the received frame is invalid. Set *size to 0, *p_data to (CPU_INT08U*)0 and return.

L7-8(5) Once every error has been handled, acquire a new data buffer to replace the one we're about to take from the descriptor. If no buffers are available set *size to 0, *p_data to (CPU_INT08U*)0, increment pdev_data current descriptor and return.

L7-8(6) Set *size to the value of the Length field of the current descriptor. This field should specify how many bytes of data were received by the descriptor.

L7-8(7) Set *p_data to the value of the data buffer of the descriptor.

L7-8(8) Set the value of the descriptor’s data buffer to the newly allocated data area.

L7-8(9) Increment the current descriptor to the next descriptor.

L7-8(10) Set *perr to NET_DEV_ERR_NONE to notify that no errors were found.

The following is the pseudo code for NetDev_RxDescPtrCurInc():

 

 

Listing 7-9 Descriptor Increment

L7-9(1) Get current pdev_data current descriptor.

L7-9(2) Set pdev_data current descriptor to the next descriptor in the current one.

Stopping the reception of packets

NetDev_Stop() is called to shutdown a network interface hardware by disabling the receiver and transmitter, disabling receive and transmit interrupts, free all receive descriptors and deallocate all transmit buffers. When the interface is stopped, you must deallocate the DMA descriptor ring. To do that, a sub-function is called NetDev_RxDescFreeAll() where each descriptor’s buffer is freed and the DMA controller control is disabled:

 

 

Listing 7-10 Deallocation of Descriptor Ring

L7-10(1) Get pdev_data's first descriptor.

L7-10(2) For each descriptor defined in .RxDescNbr in NET_DEV_CFG_ETHER:

L7-10(3) Get the address of the descriptor's buffer.

L7-10(4) Deallocate the buffer area.

L7-10(5) Set

Code Block
languagecpp
firstline1
titleListing - Descriptor ring initialization
linenumberstrue
pdesc                        = (DEV_DESC *)pdev_data->RxBufDescPtrStart;                 (1)
pdev_data->RxBufDescPtrCur   = (DEV_DESC *)pdesc;                                        (2)
pdev_data->RxBufDescPtrEnd   = (DEV_DESC *)pdesc + (pdev_cfg->RxDescNbr - 1);            (3)
for (i = 0; i < pdev_cfg->RxDescNbr; i++) {                                              (4)
    pdesc->Field   = value;                                                              (5)
    pdesc->Status  = ETH_DMA_RX_DESC_OWN;                                                (6)
    pdesc->Buf     = NetBuf_GetDataPtr((NET_IF        *)pif,                             (7)
                                       (NET_TRANSACTION)NET_TRANSACTION_RX,
                                       (NET_BUF_SIZE   )NET_IF_ETHER_FRAME_MAX_SIZE,
                                       (NET_BUF_SIZE   )NET_IF_IX_RX,
                                       (NET_BUF_SIZE  *)0,
                                       (NET_BUF_SIZE  *)0,
                                       (NET_TYPE      *)0,
                                       (NET_ERR       *)perr);
    if (*perr != NET_BUF_ERR_NONE) {                                                     (8)
         return;
    }
    pdesc->Next = (DEV_DESC *)(pdesc + 1);                                               (9)
    pdesc++;                                                                             (10)
}  


Panel
bgColor#f0f0f0

(1) Initialize the descriptor pointer to the first Rx buffer descriptor of pdev_data.

(2) Initialize current descriptor pointer of pdev_data to the first Rx buffer descriptor of pdev_data.

(3) Initialize last descriptor pointer of pdev_data to the last descriptor declared using pointer arithmetic and the Rx descriptor number defined by RxDescNbr in NET_DEV_CFG_ETHER.

(4) Repeat for each descriptor defined in .RxDescNbr in NET_DEV_CFG_ETHER.

(5) Initialize the description fields to their initial value as defined by the DMA Descriptor's documentation in the device data sheet. There might be more than a single field to define depending of the specifications of the DMA used (a field describing the size of the associated data buffer might be present and required to be initialized to the length of the requested buffer area below.)

(6) Initialize the status bit of the descriptor to specify that it is owned by the DMA engine (not by the software).

(7) Call NetBuf_GetDataPtr() to get a buffer area and initialize the descriptor's buffer start address to the address of the buffer area.

(8) If an error occurred during the allocation of a buffer area, return, as it might mean that there is an issue with the values declared in NET_DEV_CFG_ETHER and the available device memory or heap size.

(9) Initialize the next descriptor location of the current descriptor to the next descriptor using pointer arithmetic.

(10) Increment the descriptor using pointer arithmetic.



Once the Rx descriptor ring is ready, you have to configure controller registers to enable the controller reception. Controller's interrupt generation should be enabled for the following events: reception of a packet with and without errors and completed transmission of a packet with and without errors.

What Needs to be Done in the ISR for Reception

NetDev_ISR_Handler() is the function called by the IF layer when a Ethernet related ISR is generated and handled by the BSP layer. When an Rx ISR occurs, only NetOS_IF_RxTaskSignal() has to be called. Nothing has to be done on RxBufDescPtrCur. The complete receive process is delayed in order to have the fastest ISR handler as possible. If an error occurred on RX, you can increment driver statistics into the ISR handler or into NetDev_Rx(), it’s up to you to determine which of the cases is best. You must always signal the core that a packet is received using NetOS_IF_RxTaskSignal(). If you fail to notify the core for each packet, a buffer leak will occur and performance will degrade. NetDev_Rx() will discard the packet and it will say to the µC/TCP-IP module that the packet is received with an error.

Moving Buffers from the Device to the TCP-IP Stack using DMA

NetDev_Rx() is called by core once a NetOS_IF_RxTaskSignal() call has been made to recover the received packet. If data received is valid, this function must replace the buffer of the current descriptor with a free buffer. Also, the current descriptor must be restarted (owned by the DMA) to be able to receive again. RxBufDescPtrCur must be moved to point on the next descriptor. The sub-function NetDev_RxDescPtrCurInc() is called to restart the current descriptor and to move the pointer to the next descriptor. If an error has occurred, you have to set data and length pointers to 0 and return an error. If there is no free Rx buffer available, the packet must be discarded by leaving the current data buffer assigned to the DMA, increment the current descriptor and return an error to the µC/TCP-IP module. Here is some pseudo code of NetDev_Rx():

Code Block
languagecpp
firstline1
titleListing - Packet Reception
linenumberstrue
static  void  NetDev_Rx (NET_IF       *pif,
                         CPU_INT08U  **p_data,
                         CPU_INT16U   *size,
                         NET_ERR      *perr)
{
    pdesc = (DEV_DESC *)pdev_data->RxBufDescPtrStart;                        (1)
    
    if (pdesc owned by DMA) {                                                (2)
       *perr   =  NET_DEV_ERR_RX;
       *size   =  0;
       *p_data = (CPU_INT08U *)0;
        return;
    }
    
    if (is Rx error) {                                                       (3)
        if needed, restart DMA;
        if needed, NetDev_RxDescPtrCurInc();
       *perr   =  NET_DEV_ERR_RX;
       *size   =  0;
       *p_data = (CPU_INT08U *)0;
        return;    
    }
    
    if (is Data length valid) {                                              (4)
        if needed, NetDev_RxDescPtrCurInc();
       *perr   =  NET_DEV_ERR_INVALID_SIZE;
       *size   =  0;
       *p_data = (CPU_INT08U *)0;
        return;    
    }

    pbuf_new = NetBuf_GetDataPtr(..., perr);                                   (5)
    if (*perr != NET_BUF_ERR_NONE) {
        NetDev_RxDescPtrCurInc();
       *size   =  0;
       *p_data = (CPU_INT08U *)0;
        return;        
    }

    
   *size       =  pdesc->Length;                                             (6)
   *p_data     = (CPU_INT08U *)pdesc->Buf;                                   (7)
    pdesc->Buf = pbuf_new;                                                   (8)
    NetDev_RxDescPtrCurInc();                                                (9)
   *perr = NET_DEV_ERR_NONE;                                                 (10)
}


Panel
borderColor#f0f0f0

(1)  Obtain pointer to the next ready descriptor.

(2) If this descriptor is owned by the DMA (e.g., the DMA is currently receiving data or hasn't started receiving data yet). The descriptor has to be owned by the software to be processed. If owned by the DMA, set *perr to NET_DEV_ERR_RX signaling to the interrupt that there was an error within the reception. Set *size to 0, *p_data to (CPU_INT08U*)0 and return.

(3) If a reception error is reported in the descriptor set *perr to NET_DEV_ERR_RX notifying that an error occurred within the reception. Set *size to 0, *p_data to (CPU_INT08U*)0 and return.

(4) If the frame length is either run or overrun set *perr to NET_DEV_ERR_INVALID_SIZE signaling that the size of the received frame is invalid. Set *size to 0, *p_data to (CPU_INT08U*)0 and return.

(5) Once every error has been handled, acquire a new data buffer to replace the one we're about to take from the descriptor. If no buffers are available set *size to 0, *p_data to (CPU_INT08U*)0, increment pdev_data current descriptor and return.

(6) Set *size to the value of the Length field of the current descriptor. This field should specify how many bytes of data were received by the descriptor.

(7) Set *p_data to the value of the data buffer of the descriptor.

(8) Set the value of the descriptor’s data buffer to the newly allocated data area.

(9) Increment the current descriptor to the next descriptor.

(10) Set *perr to NET_DEV_ERR_NONE to notify that no errors were found.


The following is the pseudo code for NetDev_RxDescPtrCurInc() :

       

Code Block
languagecpp
firstline1
titleListing - Descriptor Increment
linenumberstrue
pdesc                      = pdev_data->RxBufDescPtrCur;            (1)
pdev_data->RxBufDescPtrCur = pdesc→Next;                            (2)


Panel
bgColor#f0f0f0

(1) Get current pdev_data current descriptor.

(2) Set pdev_data current descriptor to the next descriptor in the current one.



Stopping the Reception of Packets

NetDev_Stop() is called to shutdown a network interface hardware by disabling the receiver and transmitter, disabling receive and transmit interrupts, free all receive descriptors and deallocate all transmit buffers. When the interface is stopped, you must deallocate the DMA descriptor ring. To do that, a sub-function is called NetDev_RxDescFreeAll() where each descriptor’s buffer is freed and the DMA controller control is disabled:

Code Block
linenumberstrue
pdesc  = pdev_data->RxBufDescPtrStart;                                       (1)
for (i = 0; i < pdev_cfg->RxDescNbr; i++) {                                  (2)
    pdesc_data = (CPU_INT08U *)(pdesc->Addr);                                (3)
    NetBuf_FreeBufDataAreaRx(pif->Nbr, pdesc_data);                          (4)
    pdesc->Status = Not owned by the controller                              (5)
    pdesc++;                                                                 (6)
}


Panel
bgColor#f0f0f0

(1) Get pdev_data's first descriptor.

(2) For each descriptor defined in .RxDescNbr in NET_DEV_CFG_ETHER:

(3) Get the address of the descriptor's buffer.

(4) Deallocate the buffer area.

(5) Set the status of the descriptor to be owned by the software (to disable reception on that descriptor).

...

(6)

...

 Increment the current descriptor using pointer arithmetic.