In the following you will find a modified version of the vendor-provided STM32H743 low level lwIP Ethernet driver. Both RX and TX functions have been modified so that timestamp capture upon reception and transmission are also supported.
The complete file is accessible is included at the end of this page.
Several functions were modified and variables were declared in order to pass transmission timestamp back to the application through invoking callbacks.
An array was declared to temporarily hold the addresses where the transmit timestamps will be saved right after the packet transmission has completed.
A procedure was added that writes the transmit timestamp to their designated place. The routine is invoked whenever a frame transmission is done.
If driver operates in polled mode, calling the writeback routine can be placed instead into the following function:
#include "stm32h7xx_hal.h"
#include "lwip/timeouts.h"
#include "netif/ethernet.h"
#include "netif/etharp.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "lwip/tcpip.h"
#include "ethernetif.h"
#include "../Components/lan8742/lan8742.h"
#include <string.h>
#include "utils.h"
#define TIME_WAITING_FOR_INPUT ( osWaitForever )
#define INTERFACE_THREAD_STACK_SIZE ( 350 )
#define IFNAME0 's'
#define IFNAME1 't'
#define ETH_RX_BUFFER_SIZE (1536UL)
#define ETH_DMA_TRANSMIT_TIMEOUT (20U)
#if defined ( __ICCARM__ )
#pragma location=0x30040000
ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT];
#pragma location=0x30040060
ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT];
#pragma location=0x30040200
uint8_t Rx_Buff[ETH_RX_DESC_CNT][ETH_RX_BUFFER_SIZE];
#elif defined ( __CC_ARM )
__attribute__((section(
".RxDecripSection"))) ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT];
__attribute__((section(".TxDecripSection"))) ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT];
__attribute__((section(".RxArraySection"))) uint8_t Rx_Buff[ETH_RX_DESC_CNT][ETH_RX_BUFFER_SIZE];
#elif defined ( __GNUC__ )
ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT]
__attribute__((section(
".RxDecripSection")));
ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT]
__attribute__((section(
".TxDecripSection")));
uint8_t Rx_Buff[ETH_RX_DESC_CNT][ETH_RX_BUFFER_SIZE]
__attribute__((section(
".RxArraySection")));
uint8_t LwIP_HEAP[20*1024]
__attribute__((section(
".LwIPHEAP")));
struct pbuf *ppWriteBackPBufs[ETH_TX_DESC_CNT];
#endif
ETH_TxPacketConfig TxConfig;
lan8742_Object_t LAN8742;
osSemaphoreId RxPktSemaphore = NULL;
static void ethernetif_input( void const * argument );
u32_t sys_now(void);
void pbuf_free_custom(struct pbuf *p);
int32_t ETH_PHY_IO_Init(void);
int32_t ETH_PHY_IO_DeInit (void);
int32_t ETH_PHY_IO_ReadReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t *pRegVal);
int32_t ETH_PHY_IO_WriteReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t RegVal);
int32_t ETH_PHY_IO_GetTick(void);
lan8742_IOCtx_t LAN8742_IOCtx = {ETH_PHY_IO_Init,
ETH_PHY_IO_DeInit,
ETH_PHY_IO_WriteReg,
ETH_PHY_IO_ReadReg,
ETH_PHY_IO_GetTick};
LWIP_MEMPOOL_DECLARE(RX_POOL, 10, sizeof(struct pbuf_custom), "Zero-copy RX PBUF pool");
void ethernetif_write_back_tx_timestamps() {
for (size_t j = 0; j < ETH_TX_DESC_CNT; j++) {
ETH_DMADescTypeDef * pDesc = &DMATxDscrTab[j];
struct pbuf * pPBuf = ppWriteBackPBufs[j];
if ((pPBuf != NULL) && (!(pDesc->DESC3 & ETH_DMATXNDESCWBF_OWN)) && (pDesc->DESC3 & ETH_DMATXNDESCWBF_LD) && (pDesc->DESC3 & ETH_DMATXNDESCWBF_TTSS)) {
pPBuf->time_s = pDesc->DESC1;
pPBuf->time_ns = pDesc->DESC0;
ppWriteBackPBufs[j] = NULL;
pDesc->DESC3 &= ~ETH_DMATXNDESCWBF_TTSS;
if (pPBuf->ts_writeback_addr[0] != NULL) {
*pPBuf->ts_writeback_addr[0] = pPBuf->time_s;
}
if (pPBuf->ts_writeback_addr[1] != NULL) {
*pPBuf->ts_writeback_addr[1] = pPBuf->time_ns;
}
if (pPBuf->tx_cb) {
pPBuf->tx_cb(pPBuf);
}
pPBuf->tx_cb = NULL;
pPBuf->ts_writeback_addr[0] = NULL;
pPBuf->ts_writeback_addr[1] = NULL;
}
}
}
void HAL_ETH_TxCpltCallback(ETH_HandleTypeDef * heth) {
ethernetif_write_back_tx_timestamps();
}
static void low_level_init(struct netif *netif)
{
uint32_t idx, duplex, speed = 0;
int32_t PHYLinkState;
ETH_MACConfigTypeDef MACConf;
uint8_t macaddress[6]= {ETH_MAC_ADDR0, ETH_MAC_ADDR1, ETH_MAC_ADDR2, ETH_MAC_ADDR3, ETH_MAC_ADDR4, ETH_MAC_ADDR5};
EthHandle.Init.MediaInterface = HAL_ETH_RMII_MODE;
EthHandle.Init.RxBuffLen = ETH_RX_BUFFER_SIZE;
netif->hwaddr_len = ETH_HWADDR_LEN;
netif->hwaddr[0] = ETH_MAC_ADDR0;
netif->hwaddr[1] = ETH_MAC_ADDR1;
netif->hwaddr[2] = ETH_MAC_ADDR2;
netif->hwaddr[3] = ETH_MAC_ADDR3;
netif->hwaddr[4] = ETH_MAC_ADDR4;
netif->hwaddr[5] = ETH_MAC_ADDR5;
netif->mtu = ETH_MAX_PAYLOAD;
netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP;
for(idx = 0; idx < ETH_RX_DESC_CNT; idx ++)
{
HAL_ETH_DescAssignMemory(&
EthHandle, idx, Rx_Buff[idx], NULL);
}
LWIP_MEMPOOL_INIT(RX_POOL);
memset(&TxConfig, 0 , sizeof(ETH_TxPacketConfig));
TxConfig.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;
TxConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
TxConfig.CRCPadCtrl = ETH_CRC_PAD_INSERT;
RxPktSemaphore = xSemaphoreCreateBinary();
osThreadDef(EthIf, ethernetif_input, osPriorityRealtime, 0, INTERFACE_THREAD_STACK_SIZE);
osThreadCreate (osThread(EthIf), netif);
LAN8742_RegisterBusIO(&LAN8742, &LAN8742_IOCtx);
LAN8742_Init(&LAN8742);
PHYLinkState = LAN8742_GetLinkState(&LAN8742);
if(PHYLinkState <= LAN8742_STATUS_LINK_DOWN)
{
netif_set_link_down(netif);
netif_set_down(netif);
}
else
{
switch (PHYLinkState)
{
case LAN8742_STATUS_100MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
case LAN8742_STATUS_100MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
case LAN8742_STATUS_10MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_10M;
break;
case LAN8742_STATUS_10MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_10M;
break;
default:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
}
MACConf.DuplexMode = duplex;
MACConf.Speed = speed;
netif_set_up(netif);
netif_set_link_up(netif);
}
}
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
uint32_t i=0;
struct pbuf *q;
err_t errval = ERR_OK;
ETH_BufferTypeDef Txbuffer[ETH_TX_DESC_CNT];
memset(Txbuffer, 0 , ETH_TX_DESC_CNT*sizeof(ETH_BufferTypeDef));
for(q = p; q != NULL; q = q->next)
{
if(i >= ETH_TX_DESC_CNT)
return ERR_IF;
Txbuffer[i].buffer = q->payload;
Txbuffer[i].len = q->len;
if(i>0)
{
Txbuffer[i-1].next = &Txbuffer[i];
}
if(q->next == NULL)
{
Txbuffer[i].next = NULL;
}
i++;
}
TxConfig.Length = p->tot_len;
TxConfig.TxBuffer = Txbuffer;
ppWriteBackPBufs[
EthHandle.TxDescList.CurTxDesc] = p;
HAL_ETH_Transmit(&
EthHandle, &TxConfig, ETH_DMA_TRANSMIT_TIMEOUT);
return errval;
}
static struct pbuf * low_level_input(struct netif *netif)
{
struct pbuf *p = NULL;
ETH_BufferTypeDef RxBuff[ETH_RX_DESC_CNT];
uint32_t framelength = 0, i = 0;;
struct pbuf_custom* custom_pbuf;
memset(RxBuff, 0 , ETH_RX_DESC_CNT*sizeof(ETH_BufferTypeDef));
for(i = 0; i < ETH_RX_DESC_CNT -1; i++)
{
RxBuff[i].next=&RxBuff[i+1];
}
if(HAL_ETH_GetRxDataBuffer(&
EthHandle, RxBuff) == HAL_OK)
{
HAL_ETH_GetRxDataLength(&
EthHandle, &framelength);
SCB_InvalidateDCache_by_Addr((uint32_t *)RxBuff->buffer, framelength);
custom_pbuf = (struct pbuf_custom*)LWIP_MEMPOOL_ALLOC(RX_POOL);
if(custom_pbuf != NULL)
{
custom_pbuf->custom_free_function = pbuf_free_custom;
p = pbuf_alloced_custom(PBUF_RAW, framelength, PBUF_REF, custom_pbuf, RxBuff->buffer, framelength);
p->time_s = RxBuff->ts_sec;
p->time_ns = RxBuff->ts_nsec;
}
}
return p;
}
void ethernetif_input( void const * argument )
{
struct pbuf *p;
struct netif *netif = (struct netif *) argument;
for( ;; )
{
if (osSemaphoreWait( RxPktSemaphore, TIME_WAITING_FOR_INPUT)==osOK)
{
do
{
p = low_level_input( netif );
if (p != NULL)
{
if (netif->input( p, netif) != ERR_OK )
{
pbuf_free(p);
}
}
}while(p!=NULL);
ethernetif_write_back_tx_timestamps();
}
}
}
err_t ethernetif_init(struct netif *netif)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME
netif->hostname = "lwip";
#endif
MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
netif->output = etharp_output;
netif->linkoutput = low_level_output;
low_level_init(netif);
return ERR_OK;
}
void pbuf_free_custom(struct pbuf *p)
{
struct pbuf_custom* custom_pbuf = (struct pbuf_custom*)p;
LWIP_MEMPOOL_FREE(RX_POOL, custom_pbuf);
}
u32_t sys_now(void)
{
return HAL_GetTick();
}
void HAL_ETH_MspInit(ETH_HandleTypeDef *heth)
{
GPIO_InitTypeDef GPIO_InitStructure;
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_13;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_11 | GPIO_PIN_13;
HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0);
HAL_NVIC_EnableIRQ(ETH_IRQn);
__HAL_RCC_ETH1MAC_CLK_ENABLE();
__HAL_RCC_ETH1TX_CLK_ENABLE();
__HAL_RCC_ETH1RX_CLK_ENABLE();
}
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
{
osSemaphoreRelease(RxPktSemaphore);
}
int32_t ETH_PHY_IO_Init(void)
{
return 0;
}
int32_t ETH_PHY_IO_DeInit (void)
{
return 0;
}
int32_t ETH_PHY_IO_ReadReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t *pRegVal)
{
if(HAL_ETH_ReadPHYRegister(&
EthHandle, DevAddr, RegAddr, pRegVal) != HAL_OK)
{
return -1;
}
return 0;
}
int32_t ETH_PHY_IO_WriteReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t RegVal)
{
if(HAL_ETH_WritePHYRegister(&
EthHandle, DevAddr, RegAddr, RegVal) != HAL_OK)
{
return -1;
}
return 0;
}
int32_t ETH_PHY_IO_GetTick(void)
{
return HAL_GetTick();
}
void ethernet_link_thread( void const * argument )
{
ETH_MACConfigTypeDef MACConf;
int32_t PHYLinkState;
uint32_t linkchanged = 0, speed = 0, duplex =0;
struct netif *netif = (struct netif *) argument;
for(;;)
{
PHYLinkState = LAN8742_GetLinkState(&LAN8742);
if(netif_is_link_up(netif) && (PHYLinkState <= LAN8742_STATUS_LINK_DOWN))
{
netif_set_down(netif);
netif_set_link_down(netif);
}
else if(!netif_is_link_up(netif) && (PHYLinkState > LAN8742_STATUS_LINK_DOWN))
{
switch (PHYLinkState)
{
case LAN8742_STATUS_100MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
linkchanged = 1;
break;
case LAN8742_STATUS_100MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_100M;
linkchanged = 1;
break;
case LAN8742_STATUS_10MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_10M;
linkchanged = 1;
break;
case LAN8742_STATUS_10MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_10M;
linkchanged = 1;
break;
default:
break;
}
if(linkchanged)
{
MACConf.DuplexMode = duplex;
MACConf.Speed = speed;
netif_set_up(netif);
netif_set_link_up(netif);
}
}
osDelay(100);
}
}
struct __attribute__((packed))