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.
Then, the network device driver must allocate a list of descriptors and configure each address field to point to the start address of a Receive buffer. At the same time, the network device driver initializes three pointers: one to track the current descriptor, which is expected to contain the next received frame; a second to remember the descriptor list boundaries; and a third for the descriptor list starting address.
The DMA controller is initialized and the hardware is informed of the address of descriptor list.
Reception
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 use a control data structure that indicates the transfer configuration. These data structures are called descriptors and in order to receive multiple packets at the same time, we need multiple descriptors that we arrange in a list.
We use three pointers to manage and keep track of the Rx descriptors:
RxBufDescPtrStart
This pointer doesn’t move, it always points to the first descriptor.
RxBufDescPtrCur
This pointer must track the current descriptor which data is ready to be processed.
RxBufDescPtrEnd
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 consists of setting the descriptors pointers of the NET_DEV_DATA
and filling all Receive descriptors with a Receive buffer.
The descriptors should be organized in a ring configuration. This means that each descriptor contains a pointer to the next descriptor and the last descriptor's next pointer refers to the first descriptor of the list.
You also have to initialize each descriptor. You must initialize descriptor 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:
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()
:
The following is the pseudo code for NetDev_RxDescPtrCurInc()
:
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: