an00121: Using XMOS TCP/IP Library for UDP-based Networking on XCORE-200#
Introduction#
Transmission Control Protocol/Internet Protocol (TCP/IP) is an internetworking protocol that allows cross-platform or heterogeneous networking. It manages the flow of data in packets with headers giving the source and destination information. It provides a reliable stream delivery using sequenced acknowledgment and error detection technique. TCP/IP offers the User Datagram Protocol (UDP), a minimal transport protocol, wherein the packet headers contain just enough information to forward the datagrams and their error checking. UDP does not support flow control and acknowledgment.
XMOS TCP/IP (XTCP)#
The XMOS TCP/IP component provides a IP/UDP/TCP stack that connects to the XMOS ethernet component. It enables several clients to connect to it and send and receive on multiple TCP or UDP connections. The stack has been designed for a low memory embedded programming environment and despite its low memory footprint provides a complete stack including ARP, IPv4, UDP, TCP, DHCP, IPv4LL, ICMP and IGMP protocols. The library provides two stacks (one based on the open-source uIP and one based on LWIP) with modifications to work efficiently on the XMOS architecture and communicate between tasks using xC channels. This application note is based on the uIP stack provided in the library.
Block diagram#
The application firmware organization is shown in Fig. 1.
Fig. 1 Networking with an XMOS device#
TCP/IP Application Configuration#
This section describes the configuration of the application and the TCP/IP stack used in this example.
Project Configuration#
The example uses the TCP/IP implementation in the lib_xtcp library.
It depends on lib_board_support for the PHY configuration on the
XK_EVK_XE216 board. It depends in lib_logging for
lightweight debugging using debug_printf().
These dependencies are specified in APP_DEPENDENT_MODULES in the application’s CMakeLists.txt:
set(APP_DEPENDENT_MODULES "lib_xtcp(6.2.0)"
"lib_board_support(1.3.0)"
"lib_logging(3.3.1)")
All functions and types can be found in the xtcp.h header file:
#include <xtcp.h>
Allocating hardware resources#
The TCP/IP stack connects to the RGMII task from the Ethernet library
which requires several ports to communicate with the
Ethernet PHY. These ports are declared in the main program file
(main.xc). In this demo the ports are set up for the Ethernet
PHY connected to the XK_EVK_XE216 which uses the Gigabit Ethernet
hardware shim present in the xcore device and is accessed using a predefined
struct of ports defined in the type rgmii_ports_t:
rgmii_ports_t rgmii_ports = on tile[1] : RGMII_PORTS_INITIALIZER;
The MDIO Serial Management Interface (SMI) is used to transfer management information between MAC and PHY. This interface consists of two signals which are connected to two ports:
port p_smi_mdio = on tile[1] : XS1_PORT_1C;
port p_smi_mdc = on tile[1] : XS1_PORT_1D;
Stack Selection#
For this example the uIP stack has be selected by default, to choose which stack
to use the options are either xtcp_uip() or xtcp_lwip() in
main.xc, this is best modified by changing the option in CMakeLists.txt:
# In the compiler flags define either XTCP_STACK_UIP or XTCP_STACK_LWIP
# according to which TCP stack is preferred to run the app note
set(APP_COMPILER_FLAGS ${COMPILER_FLAGS_COMMON} -DXTCP_STACK_UIP)
This will then switch the stack and configuration in main.xc as shown below:
#ifdef XTCP_STACK_LWIP
// TCP component
on tile[0] : xtcp_lwip(
i_xtcp, 1, null,
i_cfg[CFG_TO_TCP], i_rx[ETH_TO_TCP], i_tx[ETH_TO_TCP],
mac_address_phy, null, ipconfig);
#elif defined XTCP_STACK_UIP
// TCP component
on tile[0] : xtcp_uip(
i_xtcp, NUM_TCP_CLIENTS, null,
i_cfg[CFG_TO_TCP], i_rx[ETH_TO_TCP], i_tx[ETH_TO_TCP],
mac_address_phy, null, ipconfig);
#else
#error "Please define either XTCP_STACK_LWIP or XTCP_STACK_UIP in APP_COMPILER_FLAGS"
#endif
IP configuration#
The IP is configured via a structure passed to the xtcp task
to suit the network where the XMOS device is used.
To assign a static IP address initialize with an address valid for the network the device will be connected to:
static xtcp_ipconfig_t ipconfig = {
{192, 168, 200, 178}, /* IP address, 0 for DHCP */
{255, 255, 255, 0}, /* submask, 0 for DHCP */
{0, 0, 0, 0}, /* Gateway */
};
Alternatively it can be initialized to all zeros to enable DHCP/AutoIP, this will attempt to dynamically assign an IP address:
xtcp_ipconfig_t ipconfig = {
{ 0, 0, 0, 0 }, /* ip address */
{ 0, 0, 0, 0 }, /* netmask */
{ 0, 0, 0, 0 } /* gateway */
};
UDP reflector#
The final part of the application is setup in udp_reflect.xc is a set of
defines that assigns port numbers to the broadcast and incoming ports and also
assigns the broadcast address. The size of data buffer is also set. These
defines are used by the application.
// Defines
#define RX_BUFFER_SIZE 300
#define INCOMING_PORT 15533
#define BROADCAST_INTERVAL (6 * XS1_TIMER_HZ)
#define BROADCAST_PORT 15534
#define BROADCAST_ADDR {255, 255, 255, 255}
#define BROADCAST_MSG "XMOS Broadcast\n"
Application Detailed Description#
The application main() function#
For the UDP-based application example, the system comprises four tasks running on the multicore microcontroller.
The tasks perform the following operations.
The RGMII task
rgmii_ethernet_macwhich handles RGMII/Ethernet traffic.The RGMII config task
rgmii_ethernet_mac_configwhich configures the RGMII task and handles the Ethernet PHY. This task runs on a logical core and communicates with thelib_xtcpstack.The SMI task
smiwhich handles the Serial Management Interface (SMI) for the Ethernet PHY. This is used to configure the Ethernet PHY via thear8035_phy_drivertask which manages the configuration of the PHY.The XTCP server which handles the TCP/IP stack and provides the interface to the application protocol. Either
xtcp_uiporxtcp_lwip.The UDP reflector application
udp_reflectrunning in a single core.
These tasks communicate via the use of xC channels and interface connections which allow data to be passed between application code running on separate logical threads.
Below is the source code for the main function of this application,
which is taken from the source file main.xc
int main() {
xtcp_if i_xtcp[NUM_TCP_CLIENTS];
ethernet_cfg_if i_cfg[NUM_CFG_CLIENTS];
ethernet_rx_if i_rx[NUM_ETH_CLIENTS];
ethernet_tx_if i_tx[NUM_ETH_CLIENTS];
streaming chan c_rgmii_cfg;
smi_if i_smi;
par {
on tile[1] : rgmii_ethernet_mac(
i_rx, NUM_ETH_CLIENTS, i_tx, NUM_ETH_CLIENTS,
null, null, c_rgmii_cfg, rgmii_ports,
ETHERNET_DISABLE_SHAPER);
on tile[1].core[0] : rgmii_ethernet_mac_config(i_cfg, NUM_CFG_CLIENTS, c_rgmii_cfg);
on tile[1].core[0] : ar8035_phy_driver(i_smi, i_cfg[CFG_TO_PHY_DRIVER]);
on tile[1] : smi(i_smi, p_smi_mdio, p_smi_mdc);
#ifdef XTCP_STACK_LWIP
// TCP component
on tile[0] : xtcp_lwip(
i_xtcp, 1, null,
i_cfg[CFG_TO_TCP], i_rx[ETH_TO_TCP], i_tx[ETH_TO_TCP],
mac_address_phy, null, ipconfig);
#elif defined XTCP_STACK_UIP
// TCP component
on tile[0] : xtcp_uip(
i_xtcp, NUM_TCP_CLIENTS, null,
i_cfg[CFG_TO_TCP], i_rx[ETH_TO_TCP], i_tx[ETH_TO_TCP],
mac_address_phy, null, ipconfig);
#else
#error "Please define either XTCP_STACK_LWIP or XTCP_STACK_UIP in APP_COMPILER_FLAGS"
#endif
// The simple udp reflector thread
on tile[0] : udp_reflect(i_xtcp[TCP_TO_APP]);
}
return 0;
}
The UDP reflector function#
The application code for receiving UDP packets of data from a network machine
and reflecting it back to the same machine is implemented in the file
udp_reflect.xc. Further, a fixed packet is sent periodically to a broadcast IP
address. The code performing these tasks is contained within the function
udp_reflect() which is shown in the following pages:
void udp_reflect(client xtcp_if i_xtcp) {
// The connection to the remote end we are responding to
xtcp_connection_t responding_connection;
// The connection out to the broadcast address
xtcp_connection_t broadcast_connection;
xtcp_ipaddr_t broadcast_addr = BROADCAST_ADDR;
timer tmr;
unsigned int time;
// The buffers for incoming data, outgoing responses and outgoing broadcast messages
char rx_buffer[RX_BUFFER_SIZE];
char tx_buffer[RX_BUFFER_SIZE];
char broadcast_buffer[RX_BUFFER_SIZE] = BROADCAST_MSG;
// The length of the response the thread is sending
int response_len;
// The length of the broadcast message the thread is sending
int broadcast_len;
// Maintain track of two connections. Initially they are not initialized
// which can be represented by setting their ID to -1
responding_connection.id = INIT_VAL;
broadcast_connection.id = INIT_VAL;
debug_printf("Configuration: xcore-200\n");
// Instruct server to listen and create new connections on the incoming port
i_xtcp.listen(INCOMING_PORT, XTCP_PROTOCOL_UDP);
tmr :> time;
In this segment of the code you can see the following.
The network connections, broadcast address and the buffers for holding the transmitted and received data are declared.
The XTCP server is instructed to listen and create new connections on the incoming port.
while (1) {
select {
// Respond to an event from the tcp server
case i_xtcp.packet_ready():
// A temporary variable to hold connections associated with an event
xtcp_connection_t conn;
// A temporary variable to hold the length of the packet received from get_packet()
unsigned data_len = 0;
i_xtcp.get_packet(conn, rx_buffer, RX_BUFFER_SIZE, data_len);
switch (conn.event) {
case XTCP_IFUP:
// Show the IP address of the interface
union x_ip_addr ipconfig;
i_xtcp.get_ipconfig(ipconfig.xtcp);
debug_printf("dhcp: %s\n", ipaddr_ntoa(&ipconfig.ip4));
// When the interface goes up, set up the broadcast connection.
// This connection will persist while the interface is up
// and is only used for outgoing broadcast messages
i_xtcp.connect(BROADCAST_PORT, broadcast_addr, XTCP_PROTOCOL_UDP);
break;
case XTCP_IFDOWN:
Other event handling follows the above code, then the repeated broadcast.
// This is the periodic case, it occurs every BROADCAST_INTERVAL timer ticks
case tmr when timerafter(time + BROADCAST_INTERVAL) :> void:
// A broadcast message can be sent if the connection is established
// and one is not already being sent on that connection
if (broadcast_connection.id != INIT_VAL) {
debug_printf("Sending broadcast message\n");
broadcast_len = strlen(broadcast_buffer);
i_xtcp.send(broadcast_connection, broadcast_buffer, broadcast_len);
}
time += BROADCAST_INTERVAL;
break;
You can see the following in the rest of the code.
The
whileloop waits for an XTCP event and performs the appropriate function.Periodical broadcast of a fixed data is done through timer events.
The UDP example#
Building the Application#
The following section assumes you have downloaded and installed the XMOS XTC tools (see README for required version). Installation instructions can be found here. Be sure to pay attention to the section Installation of required third-party tools.
The application uses the xcommon-cmake build system as bundled with the XTC tools.
The file CMakeLists.txt in the app_an00121 directory contains the application build configuration.
To configure the build run the following from an XTC command prompt, this should only need to be run once:
cd an00121
cd app_an00121
cmake -G "Unix Makefiles" -B build
Any missing dependencies will be downloaded by the build system as part of this configure step.
Finally, the application binaries can be built using xmake:
xmake -C build
This will build the application binary app_an00121.xe in the app_an00121/bin directory.
If CMakeLists.txt or other CMake configuration files have been modified, xmake will automatically trigger cmake to regenerate the build system as required.
Demo Hardware Setup#
Connect an xTAG-3 to the XK_EVK_XE216 XSYS port.
Connect the XK_EVK_XE216 to the PC or to a network switch using an ethernet cable.
Connect 5V power to the XK_EVK_XE216 using a USB cable.
Connect the XTAG to the host PC using a USB cable.
See XMOS hardware setup for UDP demo.
Fig. 2 XMOS hardware setup for UDP demo#
Running the example#
Once the application has been built you need to download the application binary code onto the xcore development kit. Here you use the tools to load the application over JTAG onto the xcore multicore microcontroller.
From a XTC command prompt run the following command from the app_an00121 directory:
xrun ./bin/app_an00121.xe
Alternatively the binary can be programmed into the non-volatile flash memory with the command:
xflash ./bin/app_an00121.xe
When running the program with xrun you will see the output shown below:
dhcp: 192.168.200.178
Configuration: xcore-200
New broadcast connection established:1
Sending broadcast message
Sent Broadcast
Sending broadcast message
Sent Broadcast
Testing data transfer#
The UDP example can be tested by running the python script test_udp.py.
python test_udp.py 192.168.200.178
Connecting..
Connected
Sending message: hello, world
Revc'd message: b'HELLO, WORLD'
Closed
This will connect to the XMOS device at the IP address specified and send a message to the UDP port 15533. The XMOS device will respond with the same message, with the characters translated to upper case. The xrun console will display output similar to that shown below:
New connection to listening port: 15533
Got data: 12 bytes
Responding
Sent Response
Closed connection: 2
test_udp.py test script#
1#!/usr/bin/python
2# Copyright 2025 XMOS LIMITED.
3# This Software is subject to the terms of the XMOS Public Licence: Version 1.
4
5import argparse
6import socket
7
8
9parser = argparse.ArgumentParser(description='TCP tester')
10parser.add_argument('ip', type=str, help="IP address")
11args = parser.parse_args()
12
13# This simple script sends a UDP packet to port 15533 at the
14# IP address given as the first argument to the script
15# This is to test the simple UDP example XC program
16with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
17
18 sock.settimeout(5)
19
20 print("Connecting..")
21 try:
22 sock.connect((args.ip, 15533))
23 print("Connected")
24
25 msg = "hello, world"
26 print("Sending message: " + msg)
27 sock.send(bytes(msg, "ascii"))
28
29 chunk = sock.recv(20)
30 print("Revc'd message: " + str(chunk))
31
32 except socket.timeout:
33 print("No response received within the timeout period.")
34
35print("Closed")
Further reading#
XMOS XTC Tools Installation Guide
XMOS XTC Tools User Guide
XMOS application build and dependency management system; xcommon-cmake
https://www.xmos.com/file/xcommon-cmake-documentation/?version=latest
XMOS TCP/IP Component
XMOS Layer 2 Ethernet MAC Component
XMOS logging Component
TCP/IP - Internet protocol suite
IP addressing
Ethernet Type
UDP - User Datagram Protocol