Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NetX/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

nxd_ptp_client -- SOURCE: https://github.com/STMicroelectronics/stm32-mw-netxduo/tree/79232767cef83e20573f2fbc173810f9d29c8fdb/addons/ptp
782 changes: 782 additions & 0 deletions NetX/inc/nxd_ptp_client.h

Large diffs are not rendered by default.

27 changes: 17 additions & 10 deletions NetX/inc/u_nx_ethernet.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <stdint.h>
#include <stdbool.h>
#include "nx_api.h"
#include "nxd_ptp_client.h"

/* CONFIG */
#define ETH_UDP_PORT 2006 /* UDP port for communication */
Expand All @@ -18,29 +19,29 @@
#define ETH_NUMBER_OF_NODES 8 /* Number of nodes in the network. */

typedef enum {
VCU = (1 << 0), // 0b00000001
COMPUTE = (1 << 1), // 0b00000010
TPU = (1 << 2), // 0b00000100
TPU = (1 << 0), // 0b00000001
VCU = (1 << 1), // 0b00000010
COMPUTE = (1 << 2), // 0b00000100
MSB1 = (1 << 3), // 0b00001000
MSB2 = (1 << 4), // 0b00010000
MSB3 = (1 << 5), // 0b00100000
MSB4 = (1 << 6), // 0b01000000
NODE8 = (1 << 7), // 0b10000000
} ethernet_node_t;
#define ETH_IP(node) IP_ADDRESS(239, 0, 0, node)
#define ETH_IP(node) IP_ADDRESS(224, 0, 0, node)

/* These node ids are ONLY relavent to PLCA configuration.
They are meant to be used when configuring a PHY. The IDs must be sequential, and the "0" id always indicates the network's coordinator node.
They have no impact on application-level IP addresses or message processing.
They have no impact on application-level IP addresses or message processing.

For example, if you're using the LAN8670 PHY driver, you'd probably use these enum values like this:
LAN8670_PLCA_Set_Node_Count(&lan8670, PLCA_NUM_NODES);
LAN8670_PLCA_Set_Node_Id(&lan8670, PLCA_VCU) // replace 'PLCA_VCU' with whatever board it is
LAN8670_PLCA_Set_Node_Id(&lan8670, PLCA_VCU) // replace 'PLCA_VCU' with whatever board it is
*/
typedef enum {
PLCA_VCU, // 0. This is the PLCA coordinator node.
PLCA_TPU, // 0. This is the PLCA coordinator node.
PLCA_VCU,
PLCA_COMPUTE,
PLCA_TPU,
PLCA_MSB1,
PLCA_MSB2,
PLCA_MSB3,
Expand Down Expand Up @@ -88,5 +89,11 @@ ethernet_message_t ethernet_create_message(uint8_t message_id, ethernet_node_t r
*/
uint8_t ethernet_send_message(ethernet_message_t *message);

/**
* @brief Retrieves the time from PTP stack.
* @return The UTC time
*/
NX_PTP_DATE_TIME ethernet_get_time(void);

// clang-format on
#endif /* u_nx_ethernet.h */
#endif /* u_nx_ethernet.h */
6,157 changes: 6,157 additions & 0 deletions NetX/src/nxd_ptp_client.c

Large diffs are not rendered by default.

125 changes: 114 additions & 11 deletions NetX/src/u_nx_ethernet.c
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
// clang-format off
#include "u_nx_ethernet.h"
#include "nx_stm32_eth_driver.h"
#include "nxd_ptp_client.h"
#include "u_nx_debug.h"
#include "u_tx_debug.h"
#include "nx_api.h"
#include <string.h>
#include <stdio.h>

/* PRIVATE MACROS */
#define _PACKET_POOL_SIZE \
((sizeof(ethernet_message_t) + sizeof(NX_PACKET)) * ETH_MAX_PACKETS)
#define _IP_THREAD_STACK_SIZE 2048
#define _ARP_CACHE_SIZE 1024
#define _IP_THREAD_PRIORITY 1
#define _IP_NETWORK_MASK IP_ADDRESS(255, 255, 255, 0)
#define _UDP_QUEUE_MAXIMUM 12
#define _PTP_THREAD_PRIORITY 2

/* The DEFAULT_PAYLOAD_SIZE should match with RxBuffLen configured via MX_ETH_Init */
#define DEFAULT_PAYLOAD_SIZE 1524
#define NX_APP_PACKET_POOL_SIZE ((DEFAULT_PAYLOAD_SIZE + sizeof(NX_PACKET)) * 10)

/* DEVICE INFO */
typedef struct {
/* NetX Objects */
NX_UDP_SOCKET socket;
NX_PACKET_POOL packet_pool;
NX_IP ip;
NX_PTP_CLIENT ptp_client;
SHORT ptp_utc_offset;

/* Static memory for NetX stuff */
UCHAR packet_pool_memory[_PACKET_POOL_SIZE];
UCHAR packet_pool_memory[NX_APP_PACKET_POOL_SIZE];
UCHAR ip_memory[_IP_THREAD_STACK_SIZE];
UCHAR arp_cache_memory[_ARP_CACHE_SIZE];
ULONG ptp_stack[2048 / sizeof(ULONG)];

/* Device config variables */
bool is_initialized;
Expand All @@ -36,6 +44,45 @@ typedef struct {
} _ethernet_device_t;
static _ethernet_device_t device = { 0 };

/* Callback function. Called when a PTP event is processed. */
// extern UINT ptp_clock_callback(NX_PTP_CLIENT *client_ptr, UINT operation,
// NX_PTP_TIME *time_ptr, NX_PACKET *packet_ptr,
// VOID *callback_data);

/* Callback function. Called when a PTP event is processed. */
static UINT _ptp_event_callback(NX_PTP_CLIENT *ptp_client_ptr, UINT event, VOID *event_data, VOID *callback_data)
{
NX_PARAMETER_NOT_USED(callback_data);

switch (event)
{
case NX_PTP_CLIENT_EVENT_MASTER:
{
PRINTLN_WARNING("new MASTER clock!");
break;
}

case NX_PTP_CLIENT_EVENT_SYNC:
{
nx_ptp_client_sync_info_get((NX_PTP_CLIENT_SYNC *)event_data, NX_NULL, &device.ptp_utc_offset);
PRINTLN_INFO("SYNC event: utc offset=%d", device.ptp_utc_offset);
break;
}

case NX_PTP_CLIENT_EVENT_TIMEOUT:
{
PRINTLN_WARNING("Master clock TIMEOUT!");
break;
}
default:
{
break;
}
}

return 0;
}

/* Callback function. Called when an ethernet message is received. */
static void _receive_message(NX_UDP_SOCKET *socket) {
NX_PACKET *packet;
Expand Down Expand Up @@ -91,8 +138,9 @@ static void _receive_message(NX_UDP_SOCKET *socket) {
/* API FUNCTIONS */

uint8_t ethernet_init(ethernet_node_t node_id, DriverFunction driver, OnRecieve on_recieve) {

uint8_t status;
device.ptp_utc_offset = 0; // no offset to start

/* Make sure device isn't already initialized */
if(device.is_initialized) {
Expand All @@ -109,9 +157,9 @@ uint8_t ethernet_init(ethernet_node_t node_id, DriverFunction driver, OnRecieve
status = nx_packet_pool_create(
&device.packet_pool, // Pointer to the packet pool instance
"Ethernet Packet Pool", // Name
sizeof(ethernet_message_t), // Payload size (i.e. the size of each packet)
DEFAULT_PAYLOAD_SIZE, // Payload size (i.e. the size of each packet)
device.packet_pool_memory, // Pointer to the pool's memory area
_PACKET_POOL_SIZE // Size of the pool's memory area
NX_APP_PACKET_POOL_SIZE // Size of the pool's memory area
);
if(status != NX_SUCCESS) {
PRINTLN_ERROR("Failed to create packet pool (Status: %d/%s).", status, nx_status_toString(status));
Expand Down Expand Up @@ -161,11 +209,11 @@ uint8_t ethernet_init(ethernet_node_t node_id, DriverFunction driver, OnRecieve
return status;
}

/* Set up multicast groups.
/* Set up multicast groups.
* (This iterates through every possible node combination between 0b00000001 and 0b11111111.
* If any of the combinations include device.node_id, that combination gets added as a multicast group.
* This ensures that ethernet messages can be sent to all possible combinations of recipients.)
*
*
* Note: This is probably pretty inefficient. I did it so you don't have to manually set up
* multicast groups any time you want to send a message to a new combination of nodes,
* but if this setup ends up being too slow or something, feel free to get rid of it.
Expand All @@ -180,6 +228,22 @@ uint8_t ethernet_init(ethernet_node_t node_id, DriverFunction driver, OnRecieve
}
}

/* Create the PTP client instance */
status = nx_ptp_client_create(&device.ptp_client, &device.ip, 0, &device.packet_pool,
_PTP_THREAD_PRIORITY, (UCHAR *)&device.ptp_stack, sizeof(device.ptp_stack),
_nx_ptp_client_soft_clock_callback, NX_NULL);
if(status != NX_SUCCESS) {
PRINTLN_ERROR("Failed to create PTP client (Status: %d/%s).", status, nx_status_toString(status));
return status;
}

/* start the PTP client */
status = nx_ptp_client_start(&device.ptp_client, NX_NULL, 0, 0, 0, _ptp_event_callback, NX_NULL);
if(status != NX_SUCCESS) {
PRINTLN_ERROR("Failed to start PTP client (Status: %d/%s).", status, nx_status_toString(status));
return status;
}

/* Create UDP socket for broadcasting */
status = nx_udp_socket_create(
&device.ip, // IP instance
Expand Down Expand Up @@ -263,6 +327,18 @@ uint8_t ethernet_send_message(ethernet_message_t *message) {
return U_ERROR;
}

PRINTLN_INFO("got to this part of ethernet_send_message()");

/* Make sure interface is up. */
ULONG actual_status = 0;
status = nx_ip_interface_status_check(&device.ip, 0, NX_IP_LINK_ENABLED, &actual_status, 1000);
if(status != NX_SUCCESS) {
PRINTLN_ERROR("Failed to call nx_ip_interface_status_check() (Status: %d/%s).", status, nx_status_toString(status));
return U_ERROR;
} else {
PRINTLN_INFO("Succeeded calling nx_ip_interface_status_check() (Status: %d/%s).", status, nx_status_toString(status));
}

/* Allocate a packet */
status = nx_packet_allocate(
&device.packet_pool, // Packet pool
Expand All @@ -275,6 +351,8 @@ uint8_t ethernet_send_message(ethernet_message_t *message) {
return U_ERROR;
}

PRINTLN_INFO("got to nx_packet_allocate() part of send message");

/* Append message data to packet */
status = nx_packet_data_append(
packet, // Packet
Expand All @@ -289,6 +367,16 @@ uint8_t ethernet_send_message(ethernet_message_t *message) {
return U_ERROR;
}

PRINTLN_INFO("got to nx_packet_data_append() part of send message");

ULONG length = 0;
status = nx_packet_length_get(packet, &length);
if(status != NX_SUCCESS) {
PRINTLN_ERROR("Failed to call nx_packet_length_get() (Status: %d/%n).", status, nx_status_toString(status));
} else {
PRINTLN_INFO("Packet has length of %d!", length);
}

/* Send message */
status = nx_udp_socket_send(
&device.socket,
Expand All @@ -297,12 +385,27 @@ uint8_t ethernet_send_message(ethernet_message_t *message) {
ETH_UDP_PORT
);
if(status != NX_SUCCESS) {
PRINTLN_ERROR("Failed to send packet (Status: %d/%s, Message ID: %d).", status, nx_status_toString(status), message->message_id);
PRINTLN_ERROR("Failed to send packet (Status: %d/%s, Message ID: %d, Recipient ID: %d, IP Address: %d, IP components: 224.0.0.%d).", status, nx_status_toString(status), message->message_id, message->recipient_id, ETH_IP(message->recipient_id), ETH_IP(message->recipient_id));
nx_packet_release(packet);
return U_ERROR;
}

PRINTLN_INFO("Sent ethernet message (Recipient ID: %d, Message ID: %d).", message->recipient_id, message->message_id);
PRINTLN_INFO("got to nx_udp_socket_send() part of send message");

PRINTLN_INFO("Sent ethernet message (Recipient ID: %d, Message ID: %d, Message Contents: %d).", message->recipient_id, message->message_id, message->data);
return U_SUCCESS;
}
// clang-format on

NX_PTP_DATE_TIME ethernet_get_time(void) {
NX_PTP_TIME tm;
NX_PTP_DATE_TIME date;
/* read the PTP clock */
nx_ptp_client_time_get(&device.ptp_client, &tm);

/* convert PTP time to UTC date and time */
nx_ptp_client_utility_convert_time_to_date(&tm, 0, &date);

return date;
}

// clang-format on
38 changes: 33 additions & 5 deletions general/include/lan8670.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ int32_t LAN8670_Reset(lan8670_t *lan); // Performs a software reset of the LAN86
* @param setting true to enable loopback mode, false to disable it.
* @return Status.
*/
int32_t LAN8670_Loopback(lan8670_t *lan, bool setting); // Enables or disables loopback mode on the LAN8670.
int32_t LAN8670_Loopback(lan8670_t *lan, bool setting); // NOTE: This function has not been tested yet (as of 01/31/2026).

/**
* @brief Enables or disables the LAN8670's low power mode.
Expand All @@ -84,15 +84,15 @@ int32_t LAN8670_Loopback(lan8670_t *lan, bool setting); // Enables or disables l
* @param setting true to enable low power mode, false to disable it.
* @return Status.
*/
int32_t LAN8670_Low_Power_Mode(lan8670_t *lan, bool setting); // Enables or disables the LAN8670's low power mode.
int32_t LAN8670_Low_Power_Mode(lan8670_t *lan, bool setting); // NOTE: This function has not been tested yet (as of 01/31/2026).

/**
* @brief Electrically isolates the LAN8670 from MII/RMII.
* @param lan Pointer to the lan8670_t instance.
* @param setting true to isolate the device, false for normal operation.
* @return Status.
*/
int32_t LAN8670_Isolate(lan8670_t *lan, bool setting); // Electrically isolates the LAN8670 from MII/RMII.
int32_t LAN8670_Isolate(lan8670_t *lan, bool setting); // NOTE: This function has not been tested yet (as of 01/31/2026).

/**
* @brief Enables or disables the LAN8670's collision test mode.
Expand All @@ -105,7 +105,7 @@ int32_t LAN8670_Isolate(lan8670_t *lan, bool setting); // Electrically isolates
* @param setting true to enable collision test mode, false to disable it.
* @return Status.
*/
int32_t LAN8670_Collision_Test(lan8670_t *lan, bool setting); // Enables or disables the LAN8670's collision test mode.
int32_t LAN8670_Collision_Test(lan8670_t *lan, bool setting); // NOTE: This function has not been tested yet (as of 01/31/2026).

/**
* @brief Enables or disables collision detection on the LAN8670.
Expand Down Expand Up @@ -134,7 +134,7 @@ int32_t LAN8670_PLCA_On(lan8670_t *lan, bool setting);
* @param lan Pointer to the lan8670_t instance.
* @return Status.
*/
int32_t LAN8670_PLCA_Reset(lan8670_t *lan);
int32_t LAN8670_PLCA_Reset(lan8670_t *lan); // NOTE: This function has not been tested yet (as of 01/31/2026).

/**
* @brief Configures the maximum number of nodes supported on the multidrop network.
Expand Down Expand Up @@ -182,4 +182,32 @@ int32_t LAN8670_Get_Link_State(lan8670_t *lan, uint8_t *state);
* @return Status.
*/
int32_t LAN8670_RegisterBusIO(lan8670_t *lan, lan8670_IOCtx_t *ioctx);

/**
* @brief Reads the PLCA status.
*
* @param lan Pointer to the lan8670_t instance.
* @param status Buffer for the returned status value. false=The PLCA reconciliation sublayer is not regularly receiving or transmitting the BEACON, true=The PLCA reconciliation sublayer is regularly receiving or transmitting the BEACON
* @return Status (an error code, not related to the *status bool).
*/
int32_t LAN8670_PLCA_Get_Status(lan8670_t *lan, bool *status); // NOTE: This function has not been tested yet (as of 01/31/2026).

/**
* @brief Reads the PLCA TOTMR (Transmit Opportunity Timer) setting. Should be 32 bit-times by default.
*
* @param lan Pointer to the lan8670_t instance.
* @param buffer Buffer for the register value. The value has a unit of bit-times.
* @return Status.
*/
int32_t LAN8670_PLCA_ReadTOTMR(lan8670_t *lan, bool *buffer); // NOTE: This function has not been tested yet (as of 01/31/2026).

/**
* @brief Writes the PLCA TOTMR (Transmit Opportunity Timer) setting.
*
* @param lan Pointer to the lan8670_t instance.
* @param data Setting to write to the register. Should be an integer with a unit of bit-times.
* @return Status.
*/
int32_t LAN8670_PLCA_WriteTOTMR(lan8670_t *lan, uint8_t data); // NOTE: This function has not been tested yet (as of 01/31/2026).

// clang-format on
Loading