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
devwrite 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 uint32t ul
delay = sysclkget
cpuhz() / 1000 / 3 * 400;
while (ul_delay–) {
}
uint32_t ncfgr;
gmac_options_t gmac_option;
pmc_enable_periph_clk(ID_GMAC);
memset( &gmac_option, ' ', 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( NetworkBufferDescriptort * const pxDescriptor, BaseType
t bReleaseAfterSend )
{
if( bInterfaceUp )
{
/* Not interested in a call-back after TX. */
iptraceNETWORKINTERFACE
TRANSMIT();
printf(“debug: outgoing packet length = %lu bytesrn”, pxDescriptor->xDataLength);
gmacdev
write( &gsgmac
dev, GMACQUE_0, (void *)pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, NULL );
}
if( bReleaseAfterSend != pdFALSE )
{
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
}
return pdTRUE;
}
~~~
~~~
static void gmac
resettx
mem(gmacdevice
t* pdev, gmac
quelistt queue
idx)
{
Gmac *phw = p
dev->phw;
uint8
t *ptx
buff = pdev->gmac
queuelist[queue
idx].ptx
buffer;
gmactx
descriptort *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 gmacreset
rxmem(gmac
devicet* p
dev, gmacquelist
t queueidx)
{
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
devinit() 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 pgmac
dev Pointer to GMAC device instance.
* param pdev
mm Pointer to the GMAC memory management control block.
* param ptx
cb Pointer to allocated TX callback list.
*
* return GMACOK or GMAC
PARAM.
*/
static uint8t gmac
initmem(gmac
devicet* p
gmacdev, gmac
quelistt queue
idx, gmacdev
memt* p
devmm, gmac
devtx
cbt* p
txcb)
{
if (p
devmm->us
rxsize <= 1 || p
devmm->us
txsize <= 1 || p
txcb == 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
initqueue(Gmac* p
gmac, gmacdevice
t* pgmac
dev)
{
gmacdev
memt gmac
devmm;
/* 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 pgmac
dev Pointer to the GMAC device instance.
* param popt GMAC configure options.
/
void gmac_dev_init(Gmac p
gmac, gmacdevice
t* pgmac
dev, gmacoptions
t* popt)
{
gmac
devmem
t gmacdev_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(ETHERNETCONFDATA_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
gmacdev Pointer to the GMAC device instance.
* param p
buffer Pointer to the data buffer.
* param ulsize Length of the frame.
* param func
txcb Transmit callback function.
*
* return Length sent.
/
uint32_t gmac_dev_write(gmac_device_t p
gmacdev, gmac
quelistt queue
idx, void *pbuffer, uint32
t ulsize, gmac
devtx
cbt func
txcb)
{
volatile gmac
txdescriptor
t *ptx
td;
volatile gmacdev
txcb
t *pfunc
txcb;
Gmac
p_hw = p_gmac_dev->p_hw;
gmac_queue_t p
gmacqueue = &p
gmacdev->gmac
queuelist[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 * ) ptx_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;
}
~~~