|
|
/**
**************************************************************************
* @file netx_glue_at32_emac.c
* @brief NetX Duo driver for Artery AT32 series EMAC controller.
* @version V1.0.0
* @date 2025-10-12
**************************************************************************
* Copyright notice & Disclaimer
*
* This driver is a glue layer connecting the NetX Duo TCP/IP stack with
* the Artery AT32 low-level EMAC driver.
* The logic for DMA descriptor management is heavily based on the official
* Artery LwIP ethernetif.c driver.
*
**************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "nx_api.h"
#include "at32f435_437_emac.h"
#include "nx_arp.h"
#include "wk_emac.h"
#include "commons.h"
/* Defines -------------------------------------------------------------------*/
#define IFNAME0 'a'
#define IFNAME1 't'
/* Number of DMA Descriptors */
#define EMAC_RXBUFNB 6
#define EMAC_TXBUFNB 6
/* Shift constant for extracting frame length from Rx descriptor */
#define EMAC_DMARxDesc_FrameLengthShift 16
/* Global Variables ----------------------------------------------------------*/
/* NetX Duo IP and Packet Pool instances - MUST be defined in the application */
extern NX_IP ip_0;
extern NX_PACKET_POOL pool_0;
/* DMA descriptors and buffers */
static emac_dma_desc_type DMARxDscrTab[EMAC_RXBUFNB] __attribute__((aligned(4)));
static emac_dma_desc_type DMATxDscrTab[EMAC_TXBUFNB] __attribute__((aligned(4)));
static uint8_t Rx_Buff[EMAC_RXBUFNB][EMAC_MAX_PACKET_LENGTH] __attribute__((aligned(4)));
static uint8_t Tx_Buff[EMAC_TXBUFNB][EMAC_MAX_PACKET_LENGTH] __attribute__((aligned(4)));
/* MAC Address storage */
static uint8_t MACaddr[6] = {0x00, 0x80, 0xE1, 0x00, 0x00, 0x00};
/* Structure for managing received frames, ported from LwIP driver */
typedef struct{
uint32_t length;
uint32_t buffer;
emac_dma_desc_type *descriptor;
emac_dma_desc_type *rx_fs_desc; /* First segment descriptor */
emac_dma_desc_type *rx_ls_desc; /* Last segment descriptor */
uint32_t g_seg_count;
}FrameTypeDef;
static FrameTypeDef rx_frame;
/* Externs for low-level driver variables and functions from at32_emac.c */
extern emac_dma_desc_type *dma_tx_desc_to_set;
extern emac_dma_desc_type *dma_rx_desc_to_get;
/* These functions are helpers from the LwIP driver, assumed to be available */
error_status emac_rxpkt_chainmode(void)
{
/* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
if((dma_rx_desc_to_get->status & EMAC_DMARXDESC_OWN) != (u32)RESET)
{
/* return error: own bit set */
return ERROR;
}
if((dma_rx_desc_to_get->status & EMAC_DMARXDESC_LS) != (u32)RESET)
{
rx_frame.g_seg_count ++;
if(rx_frame.g_seg_count == 1)
{
rx_frame.rx_fs_desc = dma_rx_desc_to_get;
}
rx_frame.rx_ls_desc = dma_rx_desc_to_get;
rx_frame.length = ((dma_rx_desc_to_get->status & EMAC_DMARXDESC_FL) >> EMAC_DMARxDesc_FrameLengthShift) - 4;
rx_frame.buffer = rx_frame.rx_fs_desc->buf1addr;
/* Selects the next DMA Rx descriptor list for next buffer to read */
dma_rx_desc_to_get = (emac_dma_desc_type*) (dma_rx_desc_to_get->buf2nextdescaddr);
return SUCCESS;
}
else if((dma_rx_desc_to_get->status & EMAC_DMARXDESC_FS) != (u32)RESET)
{
rx_frame.g_seg_count = 1;
rx_frame.rx_fs_desc = dma_rx_desc_to_get;
rx_frame.rx_ls_desc = NULL;
dma_rx_desc_to_get = (emac_dma_desc_type*) (dma_rx_desc_to_get->buf2nextdescaddr);
}
else
{
rx_frame.g_seg_count ++;
dma_rx_desc_to_get = (emac_dma_desc_type*) (dma_rx_desc_to_get->buf2nextdescaddr);
}
return ERROR;
}
error_status emac_txpkt_chainmode(uint32_t FrameLength)
{
uint32_t buf_cnt = 0, index = 0;
/* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
if((dma_tx_desc_to_set->status & EMAC_DMATXDESC_OWN) != (u32)RESET)
{
/* Return ERROR: OWN bit set */
return ERROR;
}
if(FrameLength == 0)
{
return ERROR;
}
if(FrameLength > EMAC_MAX_PACKET_LENGTH)
{
buf_cnt = FrameLength / EMAC_MAX_PACKET_LENGTH;
if(FrameLength % EMAC_MAX_PACKET_LENGTH)
{
buf_cnt += 1;
}
}
else
{
buf_cnt = 1;
}
if(buf_cnt == 1)
{
/* Setting the last segment and first segment bits (in this case a frame is transmitted in one descriptor) */
dma_tx_desc_to_set->status |= EMAC_DMATXDESC_LS | EMAC_DMATXDESC_FS;
/* Setting the Frame Length: bits[12:0] */
dma_tx_desc_to_set->controlsize = (FrameLength & EMAC_DMATXDESC_TBS1);
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
dma_tx_desc_to_set->status |= EMAC_DMATXDESC_OWN;
/* Selects the next DMA Tx descriptor list for next buffer to send */
dma_tx_desc_to_set = (emac_dma_desc_type*) (dma_tx_desc_to_set->buf2nextdescaddr);
}
else
{
for(index = 0; index < buf_cnt; index ++)
{
/* clear first and last segments */
dma_tx_desc_to_set->status &= ~(EMAC_DMATXDESC_LS | EMAC_DMATXDESC_FS);
/* set first segments */
if(index == 0)
{
dma_tx_desc_to_set->status |= EMAC_DMATXDESC_FS;
}
/* set size */
dma_tx_desc_to_set->controlsize = (EMAC_MAX_PACKET_LENGTH & EMAC_DMATXDESC_TBS1);
/* set last segments */
if(index == (buf_cnt - 1))
{
dma_tx_desc_to_set->status |= EMAC_DMATXDESC_LS;
dma_tx_desc_to_set->controlsize = ((FrameLength - ((buf_cnt-1) * EMAC_MAX_PACKET_LENGTH)) & EMAC_DMATXDESC_TBS1);
}
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
dma_tx_desc_to_set->status |= EMAC_DMATXDESC_OWN;
/* Selects the next DMA Tx descriptor list for next buffer to send */
dma_tx_desc_to_set = (emac_dma_desc_type*) (dma_tx_desc_to_set->buf2nextdescaddr);
}
}
/* When Tx Buffer unavailable flag is set: clear it and resume transmission */
if(emac_dma_flag_get(EMAC_DMA_TBU_FLAG))
{
/* Clear TBUS ETHERNET DMA flag */
emac_dma_flag_clear(EMAC_DMA_TBU_FLAG);
/* Resume DMA transmission*/
EMAC_DMA->tpd_bit.tpd = 0;
}
return SUCCESS;
}
u32 emac_getcurrenttxbuffer(void)
{
/* Return Buffer address */
return (dma_tx_desc_to_set->buf1addr);
}
/**
* @brief Updates the link states by reading the PHY status register.
* @param none
* @retval link state: 0 for link down or error, 1 for link up.
*/
uint16_t link_update(void)
{
uint16_t link_data, link_state;
if(emac_phy_register_read(PHY_ADDRESS, PHY_STATUS_REG, &link_data) == ERROR)
{
return ERROR;
}
link_state = (link_data & PHY_LINKED_STATUS_BIT)>>2;
return link_state;
}
/* Forward declarations */
static VOID at32_emac_driver_initialize(NX_IP_DRIVER *driver_req);
static VOID at32_emac_driver_enable(NX_IP_DRIVER *driver_req);
static VOID at32_emac_driver_disable(NX_IP_DRIVER *driver_req);
static VOID at32_emac_driver_packet_send(NX_IP_DRIVER *driver_req);
static VOID at32_emac_driver_get_status(NX_IP_DRIVER *driver_req);
/**
* @brief Sets the MAC address for the driver.
* @note This function should be called BEFORE nx_ip_create().
* @param mac_addr Pointer to a 6-byte MAC address array.
* @retval None
*/
void netx_mac_address_set(uint8_t* mac_addr)
{
for(int i = 0; i < 6; i++)
{
MACaddr = mac_addr;
}
}
/**
* @brief The main entry point for the NetX Duo AT32 EMAC driver.
* @param driver_req Pointer to the driver request structure.
* @retval None
*/
VOID at32_emac_driver_entry(NX_IP_DRIVER *driver_req)
{
switch (driver_req->nx_ip_driver_command)
{
case NX_LINK_INITIALIZE:
at32_emac_driver_initialize(driver_req);
break;
case NX_LINK_ENABLE:
at32_emac_driver_enable(driver_req);
break;
case NX_LINK_DISABLE:
at32_emac_driver_disable(driver_req);
break;
case NX_LINK_ARP_SEND:
case NX_LINK_RARP_SEND:
case NX_LINK_PACKET_SEND:
at32_emac_driver_packet_send(driver_req);
break;
case NX_LINK_GET_STATUS:
at32_emac_driver_get_status(driver_req);
break;
default:
/* Mark the request as not handled */
driver_req->nx_ip_driver_status = NX_UNHANDLED_COMMAND;
break;
}
}
/**
* @brief Handles the NX_LINK_INITIALIZE command.
* @param driver_req Pointer to the driver request structure.
* @retval None
*/
static VOID at32_emac_driver_initialize(NX_IP_DRIVER *driver_req)
{
NX_IP *ip_ptr = driver_req->nx_ip_driver_ptr;
UINT index;
/* --- Logic ported from low_level_init() --- */
/* Set up the MAC address in the hardware */
emac_local_address_set(MACaddr);
/* Set up the MAC address in the NetX IP interface */
ip_ptr->nx_ip_interface[0].nx_interface_physical_address_msw = (ULONG)((MACaddr[0] << 8) | MACaddr[1]);
ip_ptr->nx_ip_interface[0].nx_interface_physical_address_lsw = (ULONG)((MACaddr[2] << 24) | (MACaddr[3] << 16) | (MACaddr[4] << 8) | MACaddr[5]);
/* Set the Maximum Transfer Unit (MTU) */
ip_ptr->nx_ip_interface[0].nx_interface_ip_mtu_size = 1500;
/* Initialize Tx Descriptors list: Chain Mode */
emac_dma_descriptor_list_address_set(EMAC_DMA_TRANSMIT, DMATxDscrTab, &Tx_Buff[0][0], EMAC_TXBUFNB);
/* Initialize Rx Descriptors list: Chain Mode */
emac_dma_descriptor_list_address_set(EMAC_DMA_RECEIVE, DMARxDscrTab, &Rx_Buff[0][0], EMAC_RXBUFNB);
/* Enable Ethernet Rx interrupt for each descriptor */
for(index = 0; index < EMAC_RXBUFNB; index++)
{
emac_dma_rx_desc_interrupt_config(&DMARxDscrTab[index], TRUE);
}
#ifdef CHECKSUM_BY_HARDWARE
/* Enable checksum offloading in Tx descriptors */
for(index = 0; index < EMAC_TXBUFNB; index++)
{
DMATxDscrTab[index].status |= EMAC_DMATXDESC_CIC_TUI_FULL;
}
#endif
/* Initialize the received frame segment counter */
rx_frame.g_seg_count = 0;
/* Report success to NetX Duo */
driver_req->nx_ip_driver_status = NX_SUCCESS;
}
/**
* @brief Handles the NX_LINK_ENABLE command.
* @param driver_req Pointer to the driver request structure.
* @retval None
*/
static VOID at32_emac_driver_enable(NX_IP_DRIVER *driver_req)
{
/* Enable MAC and DMA transmission and reception */
emac_start();
/* Set the link status to UP in the IP instance */
driver_req->nx_ip_driver_ptr->nx_ip_interface[0].nx_interface_link_up = NX_TRUE;
/* Report success */
driver_req->nx_ip_driver_status = NX_SUCCESS;
}
/**
* @brief Handles the NX_LINK_DISABLE command.
* @param driver_req Pointer to the driver request structure.
* @retval None
*/
static VOID at32_emac_driver_disable(NX_IP_DRIVER *driver_req)
{
/* Disable MAC and DMA transmission and reception */
emac_stop();
/* Set the link status to DOWN in the IP instance */
driver_req->nx_ip_driver_ptr->nx_ip_interface[0].nx_interface_link_up = NX_FALSE;
/* Report success */
driver_req->nx_ip_driver_status = NX_SUCCESS;
}
/**
* @brief Handles packet transmission commands.
* @param driver_req Pointer to the driver request structure.
* @retval None
*/
static VOID at32_emac_driver_packet_send(NX_IP_DRIVER *driver_req)
{
NX_PACKET *packet_ptr = driver_req->nx_ip_driver_packet;
NX_PACKET *current_packet;
uint8_t *buffer;
uint32_t buffer_offset = 0;
/* --- Logic ported from low_level_output() --- */
/* 1. Pre-check: is the current DMA descriptor available by CPU? */
if((dma_tx_desc_to_set->status & EMAC_DMATXDESC_OWN) != RESET)
{
/* The DMA is busy. Report queue depth.
NetX will retain the packet and retry later. */
driver_req->nx_ip_driver_status = NX_TX_QUEUE_DEPTH;
return;
}
/* Get the standard AT32 EMAC buffer address */
buffer = (uint8_t *)emac_getcurrenttxbuffer();
/* 2. Copy data from NetX packet chain to EMAC DMA buffer */
for(current_packet = packet_ptr; current_packet != NULL; current_packet = current_packet->nx_packet_next)
{
/* Note: This logic assumes the total packet length fits in one EMAC buffer
(EMAC_MAX_PACKET_LENGTH, usually 1500-1536 bytes).
If you use Jumbo frames, the complex multi-buffer copy logic from
lwIP's ethernetif.c must be ported here. */
if ((buffer_offset + current_packet->nx_packet_length) <= EMAC_MAX_PACKET_LENGTH)
{
memcpy(buffer + buffer_offset, current_packet->nx_packet_prepend_ptr, current_packet->nx_packet_length);
buffer_offset += current_packet->nx_packet_length;
}
else
{
/* Packet exceeds buffer size. This shouldn't happen if MTU is set correctly.
Drop packet and report success to avoid NetX retrying an impossible packet. */
nx_packet_release(packet_ptr);
driver_req->nx_ip_driver_status = NX_SUCCESS;
return;
}
}
/* 3. Trigger the DMA to send the packet via underlying AT32 driver */
if (emac_txpkt_chainmode(packet_ptr->nx_packet_length) == SUCCESS)
{
/* Success: DMA now owns the buffer.
NetX is done with this packet, so we release it back to the pool. */
nx_packet_release(packet_ptr);
driver_req->nx_ip_driver_status = NX_SUCCESS;
}
else
{
/* Hardware failed to accept the packet (e.g., descriptor became owned by DMA
between step 1 and 3, or length was 0).
Report queue depth so NetX retries sending this packet later.
DO NOT release the packet here. */
driver_req->nx_ip_driver_status = NX_TX_QUEUE_DEPTH; /* Replaced NX_HARDWARE_ERROR */
}
}
/**
* @brief Handles the NX_LINK_GET_STATUS command.
* @param driver_req Pointer to the driver request structure.
* @retval None
*/
static VOID at32_emac_driver_get_status(NX_IP_DRIVER *driver_req)
{
/* Use the low-level link update function */
if (link_update())
{
*(driver_req->nx_ip_driver_return_ptr) = NX_TRUE;
}
else
{
*(driver_req->nx_ip_driver_return_ptr) = NX_FALSE;
}
driver_req->nx_ip_driver_status = NX_SUCCESS;
}
/**
* @brief The interrupt handler for the EMAC.
* @note This function should be called from the EMAC_IRQHandler in your interrupt vector file.
* @param None
* @retval None
*/
void netx_emac_interrupt_handler(void)
{
/* Check for the Receive Interrupt flag */
if (emac_dma_flag_get(EMAC_DMA_RI_FLAG) != RESET)
{
/* Process all received frames in the chain */
while(emac_rxpkt_chainmode() == SUCCESS)
{
NX_PACKET *packet_ptr = NX_NULL;
UINT status;
rtt_printf("[EMAC_IRQ] Frame received, driver length = %lu\r\n", rx_frame.length);
/* Allocate a NetX packet to hold the received data */
status = nx_packet_allocate(&pool_0, &packet_ptr, NX_RECEIVE_PACKET, NX_NO_WAIT);
if (status != NX_SUCCESS)
{
/* Packet pool is empty, we have to drop this frame. */
/* TODO: Add error statistics tracking here. */
}
else
{
/* Copy data from the DMA buffer into the newly allocated NetX packet */
status = nx_packet_data_append(packet_ptr, (uint8_t *)rx_frame.buffer, rx_frame.length, &pool_0, NX_NO_WAIT);
if (status != NX_SUCCESS)
{
/* Failed to append data, release the packet and drop the frame */
nx_packet_release(packet_ptr);
}
else
{
/* Data copied successfully, determine the packet type from the Ethernet header */
USHORT ether_type = (((USHORT)(*(packet_ptr->nx_packet_prepend_ptr + 12))) << 8) | ((USHORT)(*(packet_ptr->nx_packet_prepend_ptr + 13)));
/* Hand off the packet to the appropriate NetX Duo receiver */
if (ether_type == 0x0800) /* IP Packet */
{
rtt_printf("[EMAC_IRQ] IP Packet Received.\r\n"); // 添加日志
_nx_ip_packet_receive(&ip_0, packet_ptr);
}
else if (ether_type == 0x0806) /* ARP Packet */
{
rtt_printf("[EMAC_IRQ] ARP Packet Received.\r\n"); // 添加日志
_nx_arp_packet_receive(&ip_0, packet_ptr);
}
else
{
/* Unsupported packet type, release it */
rtt_printf("[EMAC_IRQ] Unknown Packet Type: 0x%04X\r\n", ether_type); // 添加日志
nx_packet_release(packet_ptr);
}
}
}
/* Regardless of success or failure, give ownership of the DMA descriptors back to the hardware */
emac_dma_desc_type* dma_rx_desc = rx_frame.rx_fs_desc;
UINT i;
for(i = 0; i < rx_frame.g_seg_count; i++)
{
dma_rx_desc->status |= EMAC_DMARXDESC_OWN;
dma_rx_desc = (emac_dma_desc_type*)(dma_rx_desc->buf2nextdescaddr);
}
rx_frame.g_seg_count = 0;
}
/* Clear the main interrupt flags after processing all packets */
emac_dma_flag_clear(EMAC_DMA_RI_FLAG);
emac_dma_flag_clear(EMAC_DMA_NIS_FLAG);
}
/* Handle other EMAC interrupts if necessary (e.g., Tx complete, errors) */
/* if(emac_dma_flag_get(EMAC_DMA_TI_FLAG) != RESET) { ... } */
}
这是写的一个glue,发现打断点一直不会进入at32_emac_driver_packet_send
|
|