tominvert wrote on Wednesday, April 12, 2017:
Hi,
I’m trying to port the provided SAM4E network interface to use with the SAME70, but I’m not getting any data out of the GMAC peripheral. I can see ARP requests being received, processed and responded to, but the packet does not appear on the ethernet bus. The data appears to be correctly formed when xNetworkInterfaceOutput and gmac_dev_write are called. I suspect that the issue is due to the additional prioritiy queues introduced with the SAME70 that weren’t present on the SAM4E, but I’m a novice at both low-level ethernet communications and DMA data transfer so I’m not sure where to look for issues. I can provide my full NetworkInterface.c and modfied gmac.c files if it helps - I can’t see how to attach files here though. I’ve posted my initialisation functions and send functions below. My functions don’t include any Phy initialisation or communications as our MCU connects directly to an ethernet switch over RMII. I’m running FreeRTOS V9.0.0, compiling in Atmel Studio with GCC.
Any help is greatly appreciated. Cheers.
static BaseType_t prvGMACInit( void )
{
/* Wait for PHY to be ready (CAT811: Max400ms). */
volatile uint32_t ul_delay = sysclk_get_cpu_hz() / 1000 / 3 * 400;
while (ul_delay--) {
}
uint32_t ncfgr;
gmac_options_t gmac_option;
pmc_enable_periph_clk(ID_GMAC);
memset( &gmac_option, '\0', sizeof( gmac_option ) );
gmac_option.uc_copy_all_frame = 0;
gmac_option.uc_no_boardcast = 0;
memcpy( gmac_option.uc_mac_addr, ucMACAddress, sizeof( gmac_option.uc_mac_addr ) );
gs_gmac_dev.p_hw = GMAC;
gmac_dev_init( GMAC, &gs_gmac_dev, &gmac_option );
gmac_set_mdc_clock(GMAC, sysclk_get_peripheral_hz());
gmac_set_speed(GMAC, true);
gmac_enable_full_duplex(GMAC, true);
gmac_select_mii_mode(GMAC, GMAC_PHY_RMII);
NVIC_SetPriority( GMAC_IRQn, configMAC_INTERRUPT_PRIORITY );
NVIC_EnableIRQ( GMAC_IRQn );
/* The GMAC driver will call a hook prvRxCallback(), which
in turn will wake-up the task by calling vTaskNotifyGiveFromISR() */
gmac_dev_set_rx_callback( &gs_gmac_dev, GMAC_QUE_0, prvRxCallback );
gmac_set_address( GMAC, 1, (uint8_t*)llmnr_mac_address );
ncfgr = GMAC_NCFGR_SPD | GMAC_NCFGR_FD;
GMAC->GMAC_NCFGR = ( GMAC->GMAC_NCFGR & ~( GMAC_NCFGR_SPD | GMAC_NCFGR_FD ) ) | ncfgr;
// Set Link Up
bInterfaceUp = true;
ioport_set_pin_level(PIN_LED_LINK, true);
return 1;
}
BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend )
{
if( bInterfaceUp )
{
/* Not interested in a call-back after TX. */
iptraceNETWORK_INTERFACE_TRANSMIT();
printf("debug: outgoing packet length = %lu bytes\r\n", pxDescriptor->xDataLength);
gmac_dev_write( &gs_gmac_dev, GMAC_QUE_0, (void *)pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, NULL );
}
if( bReleaseAfterSend != pdFALSE )
{
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
}
return pdTRUE;
}
static void gmac_reset_tx_mem(gmac_device_t* p_dev, gmac_quelist_t queue_idx)
{
Gmac *p_hw = p_dev->p_hw;
uint8_t *p_tx_buff = p_dev->gmac_queue_list[queue_idx].p_tx_buffer;
gmac_tx_descriptor_t *p_td = p_dev->gmac_queue_list[queue_idx].p_tx_dscr;
uint32_t ul_index;
uint32_t ul_address;
/* Disable TX */
gmac_enable_transmit(p_hw, 0);
/* Set up the TX descriptors */
CIRC_CLEAR(p_dev->gmac_queue_list[queue_idx].us_tx_head, p_dev->gmac_queue_list[queue_idx].us_tx_tail);
for (ul_index = 0; ul_index < p_dev->gmac_queue_list[queue_idx].us_tx_list_size; ul_index++)
{
ul_address = (uint32_t) (&(p_tx_buff[ul_index * GMAC_TX_UNITSIZE]));
p_td[ul_index].addr = ul_address;
p_td[ul_index].status.val = GMAC_TXD_USED;
}
p_td[p_dev->gmac_queue_list[queue_idx].us_tx_list_size - 1].status.val =
GMAC_TXD_USED | GMAC_TXD_WRAP;
/* Set transmit buffer queue */
if(queue_idx == GMAC_QUE_0) gmac_set_tx_queue(p_hw, (uint32_t) p_td);
else gmac_set_tx_priority_queue(p_hw, (uint32_t) p_td, queue_idx);
}
/**
* \brief Disable receiver, reset registers and descriptor list.
*
* \param p_drv Pointer to GMAC Driver instance.
*/
static void gmac_reset_rx_mem(gmac_device_t* p_dev, gmac_quelist_t queue_idx)
{
Gmac *p_hw = p_dev->p_hw;
uint8_t *p_rx_buff = p_dev->gmac_queue_list[queue_idx].p_rx_buffer;
gmac_rx_descriptor_t *pRd = p_dev->gmac_queue_list[queue_idx].p_rx_dscr;
uint32_t ul_index;
uint32_t ul_address;
/* Disable RX */
gmac_enable_receive(p_hw, 0);
/* Set up the RX descriptors */
p_dev->gmac_queue_list[queue_idx].us_rx_idx = 0;
for (ul_index = 0; ul_index < p_dev->gmac_queue_list[queue_idx].us_rx_list_size; ul_index++) {
ul_address = (uint32_t) (&(p_rx_buff[ul_index * GMAC_RX_UNITSIZE]));
pRd[ul_index].addr.val = ul_address & GMAC_RXD_ADDR_MASK;
pRd[ul_index].status.val = 0;
}
pRd[p_dev->gmac_queue_list[queue_idx].us_rx_list_size - 1].addr.val |= GMAC_RXD_WRAP;
/* Set receive buffer queue */
if(queue_idx == GMAC_QUE_0) {
gmac_set_rx_queue(p_hw, (uint32_t) pRd);
} else {
gmac_set_rx_priority_queue(p_hw, (uint32_t) pRd, queue_idx);
}
}
/**
* \brief Initialize the allocated buffer lists for GMAC driver to transfer data.
* Must be invoked after gmac_dev_init() but before RX/TX starts.
*
* \note If input address is not 8-byte aligned, the address is automatically
* adjusted and the list size is reduced by one.
*
* \param p_gmac Pointer to GMAC instance.
* \param p_gmac_dev Pointer to GMAC device instance.
* \param p_dev_mm Pointer to the GMAC memory management control block.
* \param p_tx_cb Pointer to allocated TX callback list.
*
* \return GMAC_OK or GMAC_PARAM.
*/
static uint8_t gmac_init_mem(gmac_device_t* p_gmac_dev, gmac_quelist_t queue_idx, gmac_dev_mem_t* p_dev_mm, gmac_dev_tx_cb_t* p_tx_cb)
{
if (p_dev_mm->us_rx_size <= 1 || p_dev_mm->us_tx_size <= 1 || p_tx_cb == NULL) return GMAC_PARAM;
gmac_queue_t* p_gmac_queue = &p_gmac_dev->gmac_queue_list[queue_idx];
/* Assign RX buffers */
if (((uint32_t) p_dev_mm->p_rx_buffer & 0x7) || ((uint32_t) p_dev_mm->p_rx_dscr & 0x7)) p_dev_mm->us_rx_size--;
p_gmac_queue->p_rx_buffer = (uint8_t *) ((uint32_t) p_dev_mm->p_rx_buffer & 0xFFFFFFF8);
p_gmac_queue->p_rx_dscr = (gmac_rx_descriptor_t *) ((uint32_t) p_dev_mm->p_rx_dscr & 0xFFFFFFF8);
p_gmac_queue->us_rx_list_size = p_dev_mm->us_rx_size;
/* Assign TX buffers */
if (((uint32_t) p_dev_mm->p_tx_buffer & 0x7) || ((uint32_t) p_dev_mm->p_tx_dscr & 0x7)) p_dev_mm->us_tx_size--;
p_gmac_queue->p_tx_buffer = (uint8_t *) ((uint32_t) p_dev_mm->p_tx_buffer & 0xFFFFFFF8);
p_gmac_queue->p_tx_dscr = (gmac_tx_descriptor_t *) ((uint32_t) p_dev_mm->p_tx_dscr & 0xFFFFFFF8);
p_gmac_queue->us_tx_list_size = p_dev_mm->us_tx_size;
p_gmac_queue->func_tx_cb_list = p_tx_cb;
/* Reset TX & RX */
gmac_reset_rx_mem(p_gmac_dev, queue_idx);
gmac_reset_tx_mem(p_gmac_dev, queue_idx);
return GMAC_OK;
}
static void gmac_init_queue(Gmac* p_gmac, gmac_device_t* p_gmac_dev)
{
gmac_dev_mem_t gmac_dev_mm;
/* Clear interrupts */
gmac_get_priority_interrupt_status(p_gmac, GMAC_QUE_2);
gmac_get_priority_interrupt_status(p_gmac, GMAC_QUE_1);
gmac_set_tx_priority_queue(p_gmac, (uint32_t)&gs_tx_desc_null, GMAC_QUE_2);
gmac_set_tx_priority_queue(p_gmac, (uint32_t)&gs_tx_desc_null, GMAC_QUE_1);
gmac_set_rx_priority_queue(p_gmac, (uint32_t)&gs_rx_desc_null, GMAC_QUE_2);
gmac_set_rx_priority_queue(p_gmac, (uint32_t)&gs_rx_desc_null, GMAC_QUE_1);
/* Clear interrupts */
gmac_get_interrupt_status(p_gmac);
/* Fill in GMAC device memory management */
gmac_dev_mm.p_rx_buffer = gs_uc_rx_buffer;
gmac_dev_mm.p_rx_dscr = gs_rx_desc;
gmac_dev_mm.us_rx_size = GMAC_RX_BUFFERS;
gmac_dev_mm.p_tx_buffer = gs_uc_tx_buffer;
gmac_dev_mm.p_tx_dscr = gs_tx_desc;
gmac_dev_mm.us_tx_size = GMAC_TX_BUFFERS;
gmac_init_mem(p_gmac_dev, GMAC_QUE_0, &gmac_dev_mm, gs_tx_callback);
/* Enable Rx and Tx, plus the statistics register */
gmac_enable_transmit(p_gmac, true);
gmac_enable_receive(p_gmac, true);
gmac_enable_statistics_write(p_gmac, true);
/* Set up the interrupts for transmission and errors */
gmac_enable_interrupt(p_gmac,
GMAC_IER_RLEX | /* Enable retry limit exceeded interrupt. */
GMAC_IER_RCOMP | /* Enable receive complete interrupt. */
GMAC_IER_RXUBR | /* Enable receive used bit read interrupt. */
GMAC_IER_ROVR | /* Enable receive overrun interrupt. */
GMAC_IER_TCOMP | /* Enable transmit complete interrupt. */
GMAC_IER_TUR | /* Enable transmit underrun interrupt. */
GMAC_IER_TFC | /* Enable transmit buffers exhausted in mid-frame interrupt. */
GMAC_IER_HRESP | /* Enable Hresp not OK interrupt. */
GMAC_IER_PFNZ | /* Enable pause frame received interrupt. */
GMAC_IER_PTZ); /* Enable pause time zero interrupt. */
}
/**
* \brief Initialize the GMAC driver.
*
* \param p_gmac Pointer to the GMAC instance.
* \param p_gmac_dev Pointer to the GMAC device instance.
* \param p_opt GMAC configure options.
*/
void gmac_dev_init(Gmac* p_gmac, gmac_device_t* p_gmac_dev, gmac_options_t* p_opt)
{
gmac_dev_mem_t gmac_dev_mm;
/* Disable TX & RX and more */
gmac_network_control(p_gmac, 0);
gmac_disable_interrupt(p_gmac, ~0u);
gmac_clear_statistics(p_gmac);
/* Clear all status bits in the receive status register. */
gmac_clear_rx_status(p_gmac, GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA);
/* Clear all status bits in the transmit status register */
gmac_clear_tx_status(p_gmac, GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE
| GMAC_TSR_TXGO | GMAC_TSR_TFC | GMAC_TSR_TXCOMP | GMAC_TSR_HRESP);
/* Clear interrupts */
gmac_get_interrupt_status(p_gmac);
#if !defined(ETHERNET_CONF_DATA_OFFSET)
/* Receive Buffer Offset
* Indicates the number of bytes by which the received data
* is offset from the start of the receive buffer
* which can be handy for alignment reasons */
/* Note: FreeRTOS+TCP wants to have this offset set to 2 bytes */
#error ETHERNET_CONF_DATA_OFFSET not defined, assuming 0
#endif
/* Enable the copy of data into the buffers
ignore broadcasts, and not copy FCS. */
gmac_set_config(p_gmac,
( gmac_get_config(p_gmac) & ~GMAC_NCFGR_RXBUFO_Msk ) |
GMAC_NCFGR_RFCS | /* Remove FCS, frame check sequence (last 4 bytes) */
GMAC_NCFGR_PEN | /* Pause Enable */
GMAC_NCFGR_RXBUFO( ETHERNET_CONF_DATA_OFFSET ) |
GMAC_RXD_RXCOEN );
/*
* GMAC_DCFGR_TXCOEN: (GMAC_DCFGR) Transmitter Checksum Generation Offload Enable.
* Note: the SAM4E does have RX checksum offloading
* but TX checksum offloading has NOT been implemented.
*/
p_gmac->GMAC_DCFGR |= GMAC_DCFGR_TXCOEN;
gmac_enable_copy_all(p_gmac, p_opt->uc_copy_all_frame);
gmac_disable_broadcast(p_gmac, p_opt->uc_no_boardcast);
/* Fill in GMAC device memory management */
gmac_dev_mm.p_rx_buffer = gs_uc_rx_buffer;
gmac_dev_mm.p_rx_dscr = gs_rx_desc;
gmac_dev_mm.us_rx_size = GMAC_RX_BUFFERS;
gmac_dev_mm.p_tx_buffer = gs_uc_tx_buffer;
gmac_dev_mm.p_tx_dscr = gs_tx_desc;
gmac_dev_mm.us_tx_size = GMAC_TX_BUFFERS;
gmac_init_queue(p_gmac, p_gmac_dev);
gmac_set_address(p_gmac, 0, p_opt->uc_mac_addr);
}
/**
* \brief Send ulLength bytes from pcFrom. This copies the buffer to one of the
* GMAC Tx buffers, and then indicates to the GMAC that the buffer is ready.
* If lEndOfFrame is true then the data being copied is the end of the frame
* and the frame can be transmitted.
*
* \param p_gmac_dev Pointer to the GMAC device instance.
* \param p_buffer Pointer to the data buffer.
* \param ul_size Length of the frame.
* \param func_tx_cb Transmit callback function.
*
* \return Length sent.
*/
uint32_t gmac_dev_write(gmac_device_t* p_gmac_dev, gmac_quelist_t queue_idx, void *p_buffer, uint32_t ul_size, gmac_dev_tx_cb_t func_tx_cb)
{
volatile gmac_tx_descriptor_t *p_tx_td;
volatile gmac_dev_tx_cb_t *p_func_tx_cb;
Gmac *p_hw = p_gmac_dev->p_hw;
gmac_queue_t* p_gmac_queue = &p_gmac_dev->gmac_queue_list[queue_idx];
/* Check parameter */
if (ul_size > GMAC_TX_UNITSIZE) return GMAC_PARAM;
/* Pointers to the current transmit descriptor */
p_tx_td = &p_gmac_queue->p_tx_dscr[p_gmac_queue->us_tx_head];
/* If no free TxTd, buffer can't be sent, schedule the wakeup callback */
if (CIRC_SPACE(p_gmac_queue->us_tx_head, p_gmac_queue->us_tx_tail, p_gmac_queue->us_tx_list_size) == 0)
{
if ((p_tx_td->status.val & GMAC_TXD_USED) == 0) return GMAC_TX_BUSY;
}
/* Pointers to the current Tx callback */
p_func_tx_cb = &p_gmac_queue->func_tx_cb_list[p_gmac_queue->us_tx_head];
/* Set up/copy data to transmission buffer */
if (p_buffer && ul_size)
{
/* Driver manages the ring buffer */
/* Calculating the checksum here is faster than calculating it from the GMAC buffer
* because withing p_buffer, it is well aligned */
memcpy((void *)p_tx_td->addr, p_buffer, ul_size);
// vGMACGenerateChecksum( ( uint8_t * ) p_tx_td->addr ); // Done in Hardware on SAME70
}
/* Tx callback */
*p_func_tx_cb = func_tx_cb;
/* Update transmit descriptor status */
/* The buffer size defined is the length of ethernet frame,
so it's always the last buffer of the frame. */
if( p_gmac_queue->us_tx_head == ( int32_t )( p_gmac_queue->us_tx_list_size - 1 ) )
{
/* No need to 'and' with GMAC_TXD_LEN_MASK because ul_size has been checked */
p_tx_td->status.val = ul_size | GMAC_TXD_LAST | GMAC_TXD_WRAP;
}
else
{
p_tx_td->status.val = ul_size | GMAC_TXD_LAST;
}
circ_inc32( &p_gmac_queue->us_tx_head, p_gmac_queue->us_tx_list_size );
/* Now start to transmit if it is still not done */
gmac_start_transmission(p_hw);
return GMAC_OK;
}