FreeRTOS + TCP/IP on Arm Cortexa53

You can check addresses of memory returned by pucGetUncachedMemory().

Is it possible for you to share you current code? I got zcu104 board today, so able to help by direct debugging.

Yes. Thank you for clarification. I described it generally, meaning that buffer length / number should be increased.

Maybe the typo confused you. I meant “no RX frames”. As I understood, emacps_check_rx(…) is called by Emac task if isr_events field contains EMAC_IF_RX_EVENT flag, equal to IRQ was received. In my understanding, if no valid descriptors (= no ethernet frames) were found on emacps_check_rx call, it is not an expected behavior. And it probably means that something is wrong with emac dma driver.
I will check the latest driver, thanks.

UPD: Just took a look at the last version. It is not changed much in the aspect under discussion. I believe at least one call to prvPassEthMessages() should happen on emacps_check_rx() call. Am I mistaken?

Yeah.
I will post it here tomorrow.

Here are my code:

Hello @Maxim.Vechkanov and @htibosch ,

I was able to find out yesterday why he always comes into the break at this point:

if (((xemacpsif->rxSegments[head].address & XEMACPS_RXBUF_NEW_MASK) == 0)
|| (pxDMA_rx_buffers[head] == NULL)) {
break;
}

This happens because in XEmacPs_Start he checks a version. If the Version == 2 then he starts the DMA. But unfortunately the US+ Version == 7.

void XEmacPs_Start(XEmacPs *InstancePtr)
{
	u32 Reg;

	/* Assert bad arguments and conditions */
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);

	/* Start DMA */
	/* When starting the DMA channels, both transmit and receive sides
	 * need an initialized BD list.
	 */
	if (InstancePtr->Version == 2) {
		Xil_AssertVoid(InstancePtr->RxBdRing.BaseBdAddr != 0);
		Xil_AssertVoid(InstancePtr->TxBdRing.BaseBdAddr != 0);
	XEmacPs_WriteReg(InstancePtr->Config.BaseAddress,
			   XEMACPS_RXQBASE_OFFSET,
			   InstancePtr->RxBdRing.BaseBdAddr);

	XEmacPs_WriteReg(InstancePtr->Config.BaseAddress,
			   XEMACPS_TXQBASE_OFFSET,
			   InstancePtr->TxBdRing.BaseBdAddr);
	}

	/* clear any existed int status */
	XEmacPs_WriteReg(InstancePtr->Config.BaseAddress, XEMACPS_ISR_OFFSET,
			   XEMACPS_IXR_ALL_MASK);

	/* Enable transmitter if not already enabled */
	if ((InstancePtr->Options & (u32)XEMACPS_TRANSMITTER_ENABLE_OPTION)!=0x00000000U) {
		Reg = XEmacPs_ReadReg(InstancePtr->Config.BaseAddress,
					XEMACPS_NWCTRL_OFFSET);
		if ((!(Reg & XEMACPS_NWCTRL_TXEN_MASK))==TRUE) {
			XEmacPs_WriteReg(InstancePtr->Config.BaseAddress,
					   XEMACPS_NWCTRL_OFFSET,
				   Reg | (u32)XEMACPS_NWCTRL_TXEN_MASK);
		}
	}

	/* Enable receiver if not already enabled */
	if ((InstancePtr->Options & XEMACPS_RECEIVER_ENABLE_OPTION) != 0x00000000U) {
		Reg = XEmacPs_ReadReg(InstancePtr->Config.BaseAddress,
					XEMACPS_NWCTRL_OFFSET);
		if ((!(Reg & XEMACPS_NWCTRL_RXEN_MASK))==TRUE) {
			XEmacPs_WriteReg(InstancePtr->Config.BaseAddress,
					   XEMACPS_NWCTRL_OFFSET,
				   Reg | (u32)XEMACPS_NWCTRL_RXEN_MASK);
		}
	}

        /* Enable TX and RX interrupts */
        XEmacPs_IntEnable(InstancePtr, (XEMACPS_IXR_TX_ERR_MASK |
	XEMACPS_IXR_RX_ERR_MASK | (u32)XEMACPS_IXR_FRAMERX_MASK |
	(u32)XEMACPS_IXR_TXCOMPL_MASK));

	/* Enable TX Q1 Interrupts */
	if (InstancePtr->Version > 2)
		XEmacPs_IntQ1Enable(InstancePtr, XEMACPS_INTQ1_IXR_ALL_MASK);

	/* Mark as started */
	InstancePtr->IsStarted = XIL_COMPONENT_IS_STARTED;

	return;
}

When I execute the commands for the DMA start manually, I don’t get into the break anymore and also .flags is no longer == 0 but 60.

Unfortunately I still can’t get ARP Announcement for my IP address

Unlike the previous version, US+ GEM supports multiple queues. The pointers are set using
XEmacPs_SetQueuePtr() function of Xilinx’s driver.
Also, since it has more than one descriptors queue, it is mandatory to terminate all the queues which are not used by writing a BD with deasserted “new” and asserted “wrapped” flags.

I am finally able to ping the board. But getting CRC errors: “CRC error: 1234 location 3”

How does you change the code that you can ping?

i can’t ping because of missing arp announcement.

I have rewritten RX part of dma initialization to use BD and BD Ring functions provided by Xilinx. Also copied TI’s PHY initialization from lwIP port.

NetworkInterface.c (16.1 KB) x_emacpsif.h (4.4 KB) x_emacpsif_dma.c (22.5 KB) x_emacpsif_physpeed.c (37.7 KB)

In guess it might be useful to separate PHYs and MACs related code. Some boards can have the same PHY but different MACs, and vice versa.
@htibosch, is it possible from TCP/IP stack code organization point of view and would it be useful?

Thank you for the Code.

In wireshark i got now this:

135 100.740303 Xilinx_00:01:02 Broadcast ARP 60 Who has 0.58.192.168? Tell 1.2.192.168

static const uint8_t ucIPAddress[ 4 ] = { 192, 168, 1, 12 };
static const uint8_t ucNetMask[ 4 ] = { 255, 255, 255, 0 };
static const uint8_t ucGatewayAddress[ 4 ] = { 192, 168, 1, 1 };

/* The following is the address of an OpenDNS server. */
static const uint8_t ucDNSServerAddress[ 4 ] = { 208, 67, 222, 222 };

const uint8_t ucMACAddress[ 6 ] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };

Looks like incorrect 64-bit words ordering, cause 192.168 mixed up with 1.1

1.2.192.168 is indeed swapped and should be 192.168.1.2
IPv4 addresses are always stored as uint32_t.

Zynq-7000 and I think US are all little-endian?

One could expect 1.2.3.4 to become 4.3.2.1 ( wrong endianness ), but not 3.4.1.2.

Okay. a clue where i need to change it?

Does wireshark shows other IP addresses correctly?

Do you have DHCP enabled? It looks so, otherwise why would it ask for someone else?
It looks like the board receives address 192.168.1.2 via DHCP. Could you please share the log of DHCP process? Mine looks like:

vDHCPProcess: offer C0A80185ip
vDHCPProcess: reply C0A80185ip
vDHCPProcess: offer C0A80185ip
vDHCPProcess: acked C0A80185ip

This is for 192.168.1.133 IP received

15 6.897205 RealtekS_0e:f7:9f Broadcast ARP 42 ARP Announcement for 192.168.1.10
thats from my laptop

#define ipconfigUSE_DHCP 0
#define ipconfigDHCP_REGISTER_HOSTNAME 0
#define ipconfigDHCP_USES_UNICAST 0

in my case:

ulIPAddress = 0x0c01a8c0
with is 12.1.168.192

but it needs to be
ulIPAdress = 0xc0a8010c
So i need to change it to big endian? or am I wrong?!

As for the +TCP library, both can be used. You define it in FreeRTOSIPConfig.h with either of these:

#define ipconfigBYTE_ORDER pdFREERTOS_LITTLE_ENDIAN
#define ipconfigBYTE_ORDER pdFREERTOS_BIG_ENDIAN

I use Zynq 7000 in little endian mode. That is configurable.

The library logging converts endianness, so you should see c0a8010c for 192.168.1.12.

ulIPAddress = 0x0c01a8c0
with is 12.1.168.192

Btw, 0x0c01a8c0 doesn’t have this strange swap of 16-bit words as above ( 1.2.192.168 )

How did you define ipconfigBYTE_ORDER ? And what is the endianness of your Cortex A53?

The library logging converts endianness, so you should see c0a8010c for 192.168.1.12 .

ulIPAddress = 0x0c01a8c0
with is 12.1.168.192

53 20.814811 Xilinx_00:01:02 Broadcast ARP 60 Who has 0.58.192.168? Tell 1.2.192.168
68 50.814970 Xilinx_00:01:02 Broadcast ARP 60 Who has 0.16.192.168? Tell 1.2.192.168
This i see in Wireshark

but i don’t define 1.2.192.168; 0.58.192.168 either 0.16.192.168

i use for me

void vApplicationIPNetworkEventHook(eIPCallbackEvent_t eNetworkEvent) {
	uint32_t ulIPAddress, ulNetMask, ulGatewayAddress, ulDNSServerAddress;
	char cBuffer[16];
	static BaseType_t xTasksAlreadyCreated = pdFALSE;

	/* If the network has just come up...*/
	if (eNetworkEvent == eNetworkUp) {
		/* Create the tasks that use the IP stack if they have not already been
		 created. */
		xil_printf ("Network is UP\r\n");
		        /* Print out the network configuration, which may have come from a DHCP server. */
				FreeRTOS_GetAddressConfiguration( &ulIPAddress, &ulNetMask, &ulGatewayAddress, &ulDNSServerAddress );

		        FreeRTOS_inet_ntoa( ulIPAddress, cBuffer );             xil_printf( "IP Address: %s\r\n", cBuffer );
		        FreeRTOS_inet_ntoa( ulNetMask, cBuffer );               xil_printf( "Subnet Mask: %s\r\n", cBuffer );
		        FreeRTOS_inet_ntoa( ulGatewayAddress, cBuffer );        xil_printf( "Gateway Address: %s\r\n", cBuffer );
		        FreeRTOS_inet_ntoa( ulDNSServerAddress, cBuffer );      xil_printf( "DNS Server Address: %s\r\n", cBuffer );
		        xil_printf("Static MAC Address: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\r\n", ucMACAddress[0],ucMACAddress[1],ucMACAddress[2],ucMACAddress[3],ucMACAddress[4],ucMACAddress[5]);

		if (xTasksAlreadyCreated == pdFALSE) {

			xTasksAlreadyCreated = pdTRUE;
		}

	}
}

the output from this is :

Network is UP
IP Address: 192.168.1.12
Subnet Mask: 255.255.255.0
Gateway Address: 192.168.1.160
DNS Server Address: 208.67.222.222
Static MAC Address: 0x0 0xA 0x35 0x0 0x1 0x2

ipconfigBYTE_ORDER is defined as pdFREERTOS_LITTLE_ENDIAN

And what is the endianness of your Cortex A53?

The processor can store words in memory as either:

  • Big-endian format.
  • Little-endian format.

Note

Instructions are always little-endian.

Hello, everybody,

With the support of Maxim I managed to get the FreeRTOS TCP/IP-Stack working. The network communication is successfully established. The ARP announcement for the corresponding IP address with the MAC address is passed and pinging between notebook and UltraScale+ also works.

At the moment I still get the message, which Maxim already mentioned, that you get a CRC error. I already change the FreeRTOS_TCP_IP.c file after the

pull request 2139

Debugging shows that the function `prvTCPReturnPacket, which includes the changes, is not executed.

Debugging shows that the function `prvTCPReturnPacket,
which includes the changes, is not executed.

The function prvTCPReturnPacket() is included when ipconfigUSE_TCP is defined as 1.
The function is only called once there is an active TCP connection. That is why you don’t see it executed.

A little experiment about endianness, could you add the following testing code:

union uLongLong {
	uint64_t uint64[ 1 ];
	uint32_t uint32[ 2 ];
	uint16_t uint16[ 4 ];
	uint8_t  uint8 [ 8 ];
};

void endian_test()
{
    union uLongLong u64;
    BaseType_t xIndex;
    for( xIndex = 0; xIndex < 8; xIndex++ )
    {
        u64.uint8[ xIndex ] = xIndex;
    }
    FreeRTOS_printf( ( "uint64 = %016LX\n",
        u64.uint64[ 0 ] ) );
    FreeRTOS_printf( ( "uint32 = %08lX %08lX\n",
        u64.uint32[ 0 ], u64.uint32[ 1 ] ) );
    FreeRTOS_printf( ( "uint16 = %04X %04X %04X %04X\n",
        u64.uint16[ 0 ], u64.uint16[ 1 ], u64.uint16[ 2 ], u64.uint16[ 3 ] ) );
}

For me, on a little-endian platform, it gives the following output:

uint64 = 0706050403020100
uint32 = 03020100 07060504
uint16 = 0100 0302 0504 0706

What does it say on your UltraScale?

For me, on a little-endian platform, it gives the following output:

uint64 = 0706050403020100
uint32 = 03020100 07060504
uint16 = 0100 0302 0504 0706

What does it say on your UltraScale?

I’ve got this:

uint64 = 0706050403020100
uint32 = 03020100 07060504
uint16 = 0100 0302 0504 0706