From 587556bf31aee3def134c9cdd14de426d14a44c4 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 21 Jan 2025 15:37:44 +0000 Subject: [PATCH 01/22] WIP document MAC feature set --- CHANGELOG.rst | 2 +- doc/rst/lib_ethernet.rst | 71 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f2083b29..a59df01e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,7 +9,7 @@ lib_ethernet change log * ADDED: Extended sim tests for testing RMII applications * ADDED: Support for XCommon CMake build system * ADDED: Exit command to RMII RT MAC - * RESOLVED: Build warnings even when compile successful + * ADDED: Improved MAC feature documentation * ADDED: Test for SMI * CHANGED: SMI re-write. Single port version now functional * REMOVED: Slicekit based examples because hardware is obsolete diff --git a/doc/rst/lib_ethernet.rst b/doc/rst/lib_ethernet.rst index d75b0267..ce2b074e 100644 --- a/doc/rst/lib_ethernet.rst +++ b/doc/rst/lib_ethernet.rst @@ -39,6 +39,18 @@ Various MAC blocks are available depending on the XMOS architecture selected, de - N/A +The MII MAC is available as two types; a low resource usage version which provides standard layer 2 data access to an array of clients, and a real-time version which offers additional hardware features including: + + * Hardware time-stamping of point of ingress and egress of frames supporting standards such as IEEE 802.1AS. + * Support for high priority send and receive queues and receive filtering. This allows time sensitive traffic to be prioritised over other traffic. + * Traffic shaping on egress using an IEEE 802.1Qav compliant credit based shaper. + * Configurable VLAN tag stripping on received frames. + +All RMII and RGMII implementations offer the 'real-time' features as standard. See the :ref:`rt_mac_section` section for more details. + +In addition, all MACs support client specific filtering for both source MAC address and Ethertype. See the :ref:`standard_mac_section` section for more details. + + ``lib_ethernet`` is intended to be used with `XCommon CMake `_ , the `XMOS` application build and dependency management system. @@ -114,6 +126,63 @@ The amount required depends on the feature set of the MAC. :numref:`ethernet_mac The SMI configuration API is a function call and so uses no dedicated threads. It blocks until the last bit of the transaction is complete. +|newpage| + + +.. _standard_mac_section: + +********************* +Standard MAC Features +********************* + +All MACs in this library support a number of useful features which can be configured by clients. + + * Support for multiple clients (Rx and Tx) allowing many tasks to share the MAC. + * Configurable Ethertype and MAC address filters for unicast, multicast and broadcast addresses. + * Configurable source MAC address. This may be used in conjunction with, for example, lib_otp providing a unique MAC address per chip. + * Link state detection allowing action to be taken bu higher layers in the case of link state change. + * MAC exit command. This tears down all tasks associate with the MAC and frees the memory and xcore resources used, including ports. This can be helpful in the case where ports may be shared (eg. Flash memory) allowing DFU support in package constrained systems. It may also be used to support multiple connect PHY devices where redundancy is required, without costing the chip resources to support multiple MACs. + * Separately configurable Rx and Tx buffer sizes (queues). + +Transmission of packets is via an API that blocks until the frame has been copied into the transmit queue. Reception of a packet can be a blocking call or combined with a notification allowing the client to ``select`` on the receive interface whereupon it can then receive the waiting packet. Please see the :ref:`api_section` for details on how to use the MAC. + + +|newpage| + +.. _rt_mac_section: + +********************** +Real-Time MAC Features +********************** + +In addition to all of the features outlined in the :ref:`standard_mac_section` section, real-time (RT) MACs offer enhanced features which are useful in a number of applications such as industrial control, real-time networking and Audio/Video streaming cases. These specific features are introduced below. + + +Hardware Time Stamping +====================== + +The XCORE contains architectural features supporting precise timing measurements. Specifically a 100 MHz timer is included and the RT MACs make use of this to timestamp packets at the point of ingress and egress. This 100 MHz, 32-bit timer value has a resolution of 10 nanoseconds and can be converted to nanoseconds by multiplying by 10. + +When receiving packets, a reference structure of type ``ethernet_packet_info_t`` contains the timestamp of the received packet at point of ingress. When transmitting packets, a special API which blocks until the packet has been transmitted is available, which returns the time of egress. + +These features, along with APIs to tune the ingress and egress latency offsets, can be used by higher layers such as IEEE 802.1AS (Timing and Synchronization) or PTP (IEEE 1588) to implement precise timing synchronisation across the newtork. + + +High Priority Queues +==================== + +blah + +Credit Based Shaper +=================== + +blah + + +VLAN Tag Stripping +================== + +blah |newpage| @@ -650,6 +719,8 @@ Increasing the bit clock may require use of smaller pull-up resistor(s) dependin |newpage| +.. _api_section: + *** API *** From 2f67ae00c3e7a07627c9efd6b57910b518f5e1d2 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 21 Jan 2025 16:20:38 +0000 Subject: [PATCH 02/22] Add text about queues and shaper --- doc/rst/lib_ethernet.rst | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/doc/rst/lib_ethernet.rst b/doc/rst/lib_ethernet.rst index ce2b074e..0ce4f3e4 100644 --- a/doc/rst/lib_ethernet.rst +++ b/doc/rst/lib_ethernet.rst @@ -140,7 +140,7 @@ All MACs in this library support a number of useful features which can be config * Support for multiple clients (Rx and Tx) allowing many tasks to share the MAC. * Configurable Ethertype and MAC address filters for unicast, multicast and broadcast addresses. * Configurable source MAC address. This may be used in conjunction with, for example, lib_otp providing a unique MAC address per chip. - * Link state detection allowing action to be taken bu higher layers in the case of link state change. + * Link state detection allowing action to be taken by higher layers in the case of link state change. * MAC exit command. This tears down all tasks associate with the MAC and frees the memory and xcore resources used, including ports. This can be helpful in the case where ports may be shared (eg. Flash memory) allowing DFU support in package constrained systems. It may also be used to support multiple connect PHY devices where redundancy is required, without costing the chip resources to support multiple MACs. * Separately configurable Rx and Tx buffer sizes (queues). @@ -163,20 +163,41 @@ Hardware Time Stamping The XCORE contains architectural features supporting precise timing measurements. Specifically a 100 MHz timer is included and the RT MACs make use of this to timestamp packets at the point of ingress and egress. This 100 MHz, 32-bit timer value has a resolution of 10 nanoseconds and can be converted to nanoseconds by multiplying by 10. -When receiving packets, a reference structure of type ``ethernet_packet_info_t`` contains the timestamp of the received packet at point of ingress. When transmitting packets, a special API which blocks until the packet has been transmitted is available, which returns the time of egress. +When receiving packets, a reference structure of type ``ethernet_packet_info_t`` contains the timestamp of the received packet at point of ingress. When transmitting packets, a special additional Tx API is provided which blocks until the packet has been transmitted is available, which returns the time of egress. -These features, along with APIs to tune the ingress and egress latency offsets, can be used by higher layers such as IEEE 802.1AS (Timing and Synchronization) or PTP (IEEE 1588) to implement precise timing synchronisation across the newtork. +These features, along with APIs to tune the ingress and egress latency offsets, can be used by higher layers such as IEEE 802.1AS (Timing and Synchronization) or PTP (IEEE 1588) to implement precise timing synchronisation across the network. High Priority Queues ==================== -blah +The RT MACs extend the standard client interfaces with the support of High Priority (HP) queues. These queues allow certain traffic to be received or transmitted before lower priority traffic, which is useful in real-time applications. + +The dedicated HP client interfaces use streaming channels instead of XC interfaces which provide higher performance data transfer. A dedicated channel is used for each of the receive and transmit interfaces. Streaming channels offer higher performance at the cost of occupying a dedicated switch path which may require careful consideration when the client is placed on a different channel due to the architectural limitation of a maximum of four inter-tile switch paths. A maximum of one HP client may be supported per MAC. + +A flag in the filter table can manually be set when making filter entries. This flag is then used to determine which priority client is to receive the packet. + +A dedicated API is provided to send and receive the HP packets and the MAC logic always prioritises HP packets and queues over low priority. + +The transmit HP queue is optionally rate limited using using the Credit Based Shaper which is described below. Together, these features provide the machinery required by IEEE 802.1Qav to ensure reliable, low-latency delivery of time-sensitive streams over Ethernet networks. + Credit Based Shaper =================== -blah +The Credit Based Shaper (CBD) uses the following mechanisms to manage egress rate: + + * Credits: Each port or queue is assigned a "credit" that increases or decreases over time based on the network's traffic conditions. + * Idle Slope: Determines how quickly credits increase when the queue is idle (i.e., waiting to transmit). + * Send Slope: Determines how quickly credits decrease when the queue is actively transmitting. + * Bandwidth Limitation: CBS limits the bandwidth of non-time-sensitive traffic, ensuring reserved bandwidth for high-priority streams. + + +If the credit is positive, the traffic stream is eligible for transmission. If the credit is negative, the traffic stream is paused until the credit returns to a positive state. + +The RT MACs are passed an enum when instantiated allowing enabling or disabling of the CBS. In addition the MAC provides an API which adjusts the high-priority TX queue's credit based shaper's idle slope dynamically. + +The idle slope passed is a fractional value representing the number of bits per reference timer tick in a Q16.16 format defined by ``MII_CREDIT_FRACTIONAL_BITS`` allowing very precise control over bandwidth reservation. Please see the :ref:`api_section` for details and an example of how to convert from bits-per-second to the slope argument. VLAN Tag Stripping From f9ca840dc822dbd8dfa05448becd2bc45c2fda66 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 21 Jan 2025 16:24:08 +0000 Subject: [PATCH 03/22] Add helper function example to set_egress_qav_idle_slope api --- lib_ethernet/api/ethernet.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib_ethernet/api/ethernet.h b/lib_ethernet/api/ethernet.h index 44a5e418..32165852 100644 --- a/lib_ethernet/api/ethernet.h +++ b/lib_ethernet/api/ethernet.h @@ -170,7 +170,19 @@ typedef interface ethernet_cfg_if { * This function is only available in the 10/100 Mb/s real-time and 10/100/1000 Mb/s MACs. * * \param ifnum The index of the MAC interface to set the slope - * \param slope The slope value + * \param slope The slope value in bits per 100 MHz ref timer tick in MII_CREDIT_FRACTIONAL_BITS Q format. + * + * To convert from bits-per-second to the MII_CREDIT_FRACTIONAL_BITS format for the parameter 'slope', the + * following helper function may be used: + * + * static unsigned calc_idle_slope(unsigned bits_per_second) + * { + * unsigned long long slope = ((unsigned long long) bits_per_second) << (MII_CREDIT_FRACTIONAL_BITS); + * slope = slope / XS1_TIMER_HZ; // bits that should be sent per ref timer tick + * + * return (unsigned) slope; + * } + * */ void set_egress_qav_idle_slope(size_t ifnum, unsigned slope); From 9ad88b17ebb3ae7abd1710675fd9098ba4738e7f Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 22 Jan 2025 10:37:08 +0000 Subject: [PATCH 04/22] Add VLAN stripping docs and some correctlons --- doc/rst/lib_ethernet.rst | 31 ++++++++++++++++++------------- lib_ethernet/api/ethernet.h | 2 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/doc/rst/lib_ethernet.rst b/doc/rst/lib_ethernet.rst index 0ce4f3e4..3a8bb7b7 100644 --- a/doc/rst/lib_ethernet.rst +++ b/doc/rst/lib_ethernet.rst @@ -138,14 +138,15 @@ Standard MAC Features All MACs in this library support a number of useful features which can be configured by clients. * Support for multiple clients (Rx and Tx) allowing many tasks to share the MAC. - * Configurable Ethertype and MAC address filters for unicast, multicast and broadcast addresses. - * Configurable source MAC address. This may be used in conjunction with, for example, lib_otp providing a unique MAC address per chip. + * Configurable Ethertype and MAC address filters for unicast, multicast and broadcast addresses and is configurable per client. The number of entries if configurable using ``ETHERNET_MACADDR_FILTER_TABLE_SIZE``. + * Configurable source MAC address. This may be used in conjunction with, for example, lib_otp to provide a unique MAC address per XMOS chip. * Link state detection allowing action to be taken by higher layers in the case of link state change. - * MAC exit command. This tears down all tasks associate with the MAC and frees the memory and xcore resources used, including ports. This can be helpful in the case where ports may be shared (eg. Flash memory) allowing DFU support in package constrained systems. It may also be used to support multiple connect PHY devices where redundancy is required, without costing the chip resources to support multiple MACs. * Separately configurable Rx and Tx buffer sizes (queues). + * VLAN aware packet reception. If the VLAN tag (0x8100) is seen the the header length is automatically extended by 4 octets to support the Tag Protocol Identifier (TPID) and Tag Control Information (TCI). Transmission of packets is via an API that blocks until the frame has been copied into the transmit queue. Reception of a packet can be a blocking call or combined with a notification allowing the client to ``select`` on the receive interface whereupon it can then receive the waiting packet. Please see the :ref:`api_section` for details on how to use the MAC. +In addition the RMII RT MAC supports an exit command. This tears down all tasks associated with the MAC and frees the memory and xcore resources used, including ports. This can be helpful in cases where ports may be shared (eg. Flash memory) allowing DFU support in package constrained systems. It may also be used to support multiple connect PHY devices where redundancy is required, without costing the chip resources to support multiple MACs. |newpage| @@ -161,9 +162,11 @@ In addition to all of the features outlined in the :ref:`standard_mac_section` s Hardware Time Stamping ====================== -The XCORE contains architectural features supporting precise timing measurements. Specifically a 100 MHz timer is included and the RT MACs make use of this to timestamp packets at the point of ingress and egress. This 100 MHz, 32-bit timer value has a resolution of 10 nanoseconds and can be converted to nanoseconds by multiplying by 10. +The XCORE contains architectural features supporting precise timing measurements. Specifically, a 100 MHz timer is included and the RT MACs make use of this to timestamp packets at the point of ingress and egress. This 100 MHz, 32-bit timer value has a resolution of 10 nanoseconds and the provided timestamp can be converted to nanoseconds by multiplying by 10. -When receiving packets, a reference structure of type ``ethernet_packet_info_t`` contains the timestamp of the received packet at point of ingress. When transmitting packets, a special additional Tx API is provided which blocks until the packet has been transmitted is available, which returns the time of egress. +When receiving packets, a reference to a structure of type ``ethernet_packet_info_t`` contains the timestamp of the received packet at point of ingress. + +When transmitting packets, an additional Tx API is provided for the RT MAC which blocks until the packet has been transmitted and returns the time of egress. These features, along with APIs to tune the ingress and egress latency offsets, can be used by higher layers such as IEEE 802.1AS (Timing and Synchronization) or PTP (IEEE 1588) to implement precise timing synchronisation across the network. @@ -171,21 +174,23 @@ These features, along with APIs to tune the ingress and egress latency offsets, High Priority Queues ==================== -The RT MACs extend the standard client interfaces with the support of High Priority (HP) queues. These queues allow certain traffic to be received or transmitted before lower priority traffic, which is useful in real-time applications. +The RT MACs extend the standard client interfaces with the support of High Priority (HP) queues. These queues allow certain traffic to be received or transmitted before lower priority traffic, which is useful in real-time applications where the network is shared with normal, lower priority, traffic. + +HP traffic has it's own dedicate queues inside the MAC providing separation from other traffic. -The dedicated HP client interfaces use streaming channels instead of XC interfaces which provide higher performance data transfer. A dedicated channel is used for each of the receive and transmit interfaces. Streaming channels offer higher performance at the cost of occupying a dedicated switch path which may require careful consideration when the client is placed on a different channel due to the architectural limitation of a maximum of four inter-tile switch paths. A maximum of one HP client may be supported per MAC. +The dedicated HP client interfaces use streaming channels instead of XC interfaces which provide higher performance data transfer. A dedicated channel is used for each of the receive and transmit interfaces. Streaming channels offer higher performance at the cost of occupying a dedicated switch path which may require careful consideration if the client is placed on a different tile from the MAC. This is important due to the architectural limitation of a maximum of four inter-tile switch paths between tiles. A maximum of one HP receive and transmit client are be supported per MAC. -A flag in the filter table can manually be set when making filter entries. This flag is then used to determine which priority client is to receive the packet. +A flag in the filter table can manually be set when making filter entries which is then used to determine the priority level when receiving packets. -A dedicated API is provided to send and receive the HP packets and the MAC logic always prioritises HP packets and queues over low priority. +A dedicated API is provided to send and receive the HP packets. The MAC logic always prioritises HP packets and queues over low priority. -The transmit HP queue is optionally rate limited using using the Credit Based Shaper which is described below. Together, these features provide the machinery required by IEEE 802.1Qav to ensure reliable, low-latency delivery of time-sensitive streams over Ethernet networks. +The transmit HP queue is optionally rate limited using using the Credit Based Shaper which is described below. Together, these features provide the machinery required by IEEE 802.1Qav, allowing reliable, low-latency delivery of time-sensitive streams over Ethernet networks. Credit Based Shaper =================== -The Credit Based Shaper (CBD) uses the following mechanisms to manage egress rate: +The Credit Based Shaper (CBS) uses the following mechanisms to manage egress rate: * Credits: Each port or queue is assigned a "credit" that increases or decreases over time based on the network's traffic conditions. * Idle Slope: Determines how quickly credits increase when the queue is idle (i.e., waiting to transmit). @@ -193,7 +198,7 @@ The Credit Based Shaper (CBD) uses the following mechanisms to manage egress rat * Bandwidth Limitation: CBS limits the bandwidth of non-time-sensitive traffic, ensuring reserved bandwidth for high-priority streams. -If the credit is positive, the traffic stream is eligible for transmission. If the credit is negative, the traffic stream is paused until the credit returns to a positive state. +If the credit is positive, the traffic stream is eligible for transmission. If the credit is negative, the traffic stream is paused until the credit returns to a positive state. By spreading traffic out evenly over time using a CBS, the queue size in each bridge and endpoint can be shorter, which in turn reduces the latency experienced by traffic as it flows through the system. The RT MACs are passed an enum when instantiated allowing enabling or disabling of the CBS. In addition the MAC provides an API which adjusts the high-priority TX queue's credit based shaper's idle slope dynamically. @@ -203,7 +208,7 @@ The idle slope passed is a fractional value representing the number of bits per VLAN Tag Stripping ================== -blah +In addition to standard MAC VLAN awareness of received packets when calculating payload length, the RT MAC also includes a feature to automatically strip VLAN tags. This is done inside the MAC so that the application can just treat the incoming packet as a standard Ethernet frame. VLAN stripping is dynamically controllable on a per-client basis. |newpage| diff --git a/lib_ethernet/api/ethernet.h b/lib_ethernet/api/ethernet.h index 32165852..d48de00e 100644 --- a/lib_ethernet/api/ethernet.h +++ b/lib_ethernet/api/ethernet.h @@ -175,7 +175,7 @@ typedef interface ethernet_cfg_if { * To convert from bits-per-second to the MII_CREDIT_FRACTIONAL_BITS format for the parameter 'slope', the * following helper function may be used: * - * static unsigned calc_idle_slope(unsigned bits_per_second) + * unsigned calc_idle_slope(unsigned bits_per_second) * { * unsigned long long slope = ((unsigned long long) bits_per_second) << (MII_CREDIT_FRACTIONAL_BITS); * slope = slope / XS1_TIMER_HZ; // bits that should be sent per ref timer tick From 15cfb29db84f2f0f5b1f69cb2c9a192817ef6c6f Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 22 Jan 2025 10:51:17 +0000 Subject: [PATCH 05/22] Tidy tables --- doc/rst/lib_ethernet.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/rst/lib_ethernet.rst b/doc/rst/lib_ethernet.rst index 3a8bb7b7..5f234637 100644 --- a/doc/rst/lib_ethernet.rst +++ b/doc/rst/lib_ethernet.rst @@ -15,6 +15,7 @@ Various MAC blocks are available depending on the XMOS architecture selected, de .. _ethernet_supported_macs: .. list-table:: Ethernet MAC support by XMOS device family + :widths: 30 20 20 20 20 :header-rows: 1 * - XCORE Architecture @@ -73,6 +74,7 @@ The amount required depends on the feature set of the MAC. :numref:`ethernet_mac .. _ethernet_mac_resource_usage: .. list-table:: Ethernet MAC XCORE resource usage + :widths: 25 7 25 7 9 7 :header-rows: 1 * - Configuration @@ -174,15 +176,15 @@ These features, along with APIs to tune the ingress and egress latency offsets, High Priority Queues ==================== -The RT MACs extend the standard client interfaces with the support of High Priority (HP) queues. These queues allow certain traffic to be received or transmitted before lower priority traffic, which is useful in real-time applications where the network is shared with normal, lower priority, traffic. +The RT MACs extend the standard client interfaces with the support of High Priority (HP) queues. These queues allow certain traffic to be received or transmitted before lower priority traffic, which is useful in real-time applications where the network is shared with normal, lower priority, traffic. The MAC logic always prioritises HP packets and queues over low priority. HP traffic has it's own dedicate queues inside the MAC providing separation from other traffic. The dedicated HP client interfaces use streaming channels instead of XC interfaces which provide higher performance data transfer. A dedicated channel is used for each of the receive and transmit interfaces. Streaming channels offer higher performance at the cost of occupying a dedicated switch path which may require careful consideration if the client is placed on a different tile from the MAC. This is important due to the architectural limitation of a maximum of four inter-tile switch paths between tiles. A maximum of one HP receive and transmit client are be supported per MAC. -A flag in the filter table can manually be set when making filter entries which is then used to determine the priority level when receiving packets. +A flag in the filter table can manually be set when making filter entries which is then used to determine the priority level when receiving packets. This determines which queue to use. -A dedicated API is provided to send and receive the HP packets. The MAC logic always prioritises HP packets and queues over low priority. +A dedicated API is provided to send and receive the HP packets. The transmit HP queue is optionally rate limited using using the Credit Based Shaper which is described below. Together, these features provide the machinery required by IEEE 802.1Qav, allowing reliable, low-latency delivery of time-sensitive streams over Ethernet networks. From 753e6380036b5f460da82c9baf006092d758354c Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 22 Jan 2025 17:12:29 +0000 Subject: [PATCH 06/22] typo Co-authored-by: shuchitak <38428600+shuchitak@users.noreply.github.com> --- doc/rst/lib_ethernet.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/rst/lib_ethernet.rst b/doc/rst/lib_ethernet.rst index 5f234637..9ff16945 100644 --- a/doc/rst/lib_ethernet.rst +++ b/doc/rst/lib_ethernet.rst @@ -140,7 +140,7 @@ Standard MAC Features All MACs in this library support a number of useful features which can be configured by clients. * Support for multiple clients (Rx and Tx) allowing many tasks to share the MAC. - * Configurable Ethertype and MAC address filters for unicast, multicast and broadcast addresses and is configurable per client. The number of entries if configurable using ``ETHERNET_MACADDR_FILTER_TABLE_SIZE``. + * Configurable Ethertype and MAC address filters for unicast, multicast and broadcast addresses and is configurable per client. The number of entries is configurable using ``ETHERNET_MACADDR_FILTER_TABLE_SIZE``. * Configurable source MAC address. This may be used in conjunction with, for example, lib_otp to provide a unique MAC address per XMOS chip. * Link state detection allowing action to be taken by higher layers in the case of link state change. * Separately configurable Rx and Tx buffer sizes (queues). From cc5e57382fa3c76e60f1d8885b6db03afa2ff1db Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 22 Jan 2025 17:12:48 +0000 Subject: [PATCH 07/22] typo2 Co-authored-by: shuchitak <38428600+shuchitak@users.noreply.github.com> --- doc/rst/lib_ethernet.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/rst/lib_ethernet.rst b/doc/rst/lib_ethernet.rst index 9ff16945..4fd449b8 100644 --- a/doc/rst/lib_ethernet.rst +++ b/doc/rst/lib_ethernet.rst @@ -144,7 +144,7 @@ All MACs in this library support a number of useful features which can be config * Configurable source MAC address. This may be used in conjunction with, for example, lib_otp to provide a unique MAC address per XMOS chip. * Link state detection allowing action to be taken by higher layers in the case of link state change. * Separately configurable Rx and Tx buffer sizes (queues). - * VLAN aware packet reception. If the VLAN tag (0x8100) is seen the the header length is automatically extended by 4 octets to support the Tag Protocol Identifier (TPID) and Tag Control Information (TCI). + * VLAN aware packet reception. If the VLAN tag (0x8100) is seen the header length is automatically extended by 4 octets to support the Tag Protocol Identifier (TPID) and Tag Control Information (TCI). Transmission of packets is via an API that blocks until the frame has been copied into the transmit queue. Reception of a packet can be a blocking call or combined with a notification allowing the client to ``select`` on the receive interface whereupon it can then receive the waiting packet. Please see the :ref:`api_section` for details on how to use the MAC. From 5fa83218c6be40f49cd173947d68f008ebb778e6 Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 22 Jan 2025 17:13:16 +0000 Subject: [PATCH 08/22] typo3 Co-authored-by: shuchitak <38428600+shuchitak@users.noreply.github.com> --- doc/rst/lib_ethernet.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/rst/lib_ethernet.rst b/doc/rst/lib_ethernet.rst index 4fd449b8..5136c5b7 100644 --- a/doc/rst/lib_ethernet.rst +++ b/doc/rst/lib_ethernet.rst @@ -178,7 +178,7 @@ High Priority Queues The RT MACs extend the standard client interfaces with the support of High Priority (HP) queues. These queues allow certain traffic to be received or transmitted before lower priority traffic, which is useful in real-time applications where the network is shared with normal, lower priority, traffic. The MAC logic always prioritises HP packets and queues over low priority. -HP traffic has it's own dedicate queues inside the MAC providing separation from other traffic. +HP traffic has its own dedicate queues inside the MAC providing separation from other traffic. The dedicated HP client interfaces use streaming channels instead of XC interfaces which provide higher performance data transfer. A dedicated channel is used for each of the receive and transmit interfaces. Streaming channels offer higher performance at the cost of occupying a dedicated switch path which may require careful consideration if the client is placed on a different tile from the MAC. This is important due to the architectural limitation of a maximum of four inter-tile switch paths between tiles. A maximum of one HP receive and transmit client are be supported per MAC. From e2d487373f2a7e274a0ac786ca0869ffeeda4105 Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 22 Jan 2025 17:13:39 +0000 Subject: [PATCH 09/22] typo4 Co-authored-by: shuchitak <38428600+shuchitak@users.noreply.github.com> --- doc/rst/lib_ethernet.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/rst/lib_ethernet.rst b/doc/rst/lib_ethernet.rst index 5136c5b7..a9740e90 100644 --- a/doc/rst/lib_ethernet.rst +++ b/doc/rst/lib_ethernet.rst @@ -186,7 +186,7 @@ A flag in the filter table can manually be set when making filter entries which A dedicated API is provided to send and receive the HP packets. -The transmit HP queue is optionally rate limited using using the Credit Based Shaper which is described below. Together, these features provide the machinery required by IEEE 802.1Qav, allowing reliable, low-latency delivery of time-sensitive streams over Ethernet networks. +The transmit HP queue is optionally rate limited using the Credit Based Shaper which is described below. Together, these features provide the machinery required by IEEE 802.1Qav, allowing reliable, low-latency delivery of time-sensitive streams over Ethernet networks. Credit Based Shaper From b0c37ed4ed0cff6d2d960dc194ca0e32239dd35d Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 22 Jan 2025 18:59:15 +0000 Subject: [PATCH 10/22] Review feedback --- doc/rst/lib_ethernet.rst | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/doc/rst/lib_ethernet.rst b/doc/rst/lib_ethernet.rst index a9740e90..c7abdfb7 100644 --- a/doc/rst/lib_ethernet.rst +++ b/doc/rst/lib_ethernet.rst @@ -176,41 +176,38 @@ These features, along with APIs to tune the ingress and egress latency offsets, High Priority Queues ==================== -The RT MACs extend the standard client interfaces with the support of High Priority (HP) queues. These queues allow certain traffic to be received or transmitted before lower priority traffic, which is useful in real-time applications where the network is shared with normal, lower priority, traffic. The MAC logic always prioritises HP packets and queues over low priority. +The RT MACs extend the standard client interfaces with the support of a dedicated High Priority (HP) queue. This queue allows traffic to be received or transmitted before lower priority traffic, which is useful in real-time applications where the network is shared with normal, lower priority, traffic. The MAC logic always prioritises HP packets and queues over low priority. -HP traffic has its own dedicate queues inside the MAC providing separation from other traffic. - -The dedicated HP client interfaces use streaming channels instead of XC interfaces which provide higher performance data transfer. A dedicated channel is used for each of the receive and transmit interfaces. Streaming channels offer higher performance at the cost of occupying a dedicated switch path which may require careful consideration if the client is placed on a different tile from the MAC. This is important due to the architectural limitation of a maximum of four inter-tile switch paths between tiles. A maximum of one HP receive and transmit client are be supported per MAC. +The dedicated HP client API uses streaming channels instead of XC interfaces which provide higher performance data transfer. A dedicated channel is used for each of the receive and transmit interfaces. Streaming channels offer higher performance at the cost of occupying a dedicated switch path which may require careful consideration if the client is placed on a different tile from the MAC. This is important due to the architectural limitation of a maximum of four inter-tile switch paths between tiles. A maximum of one HP receive and transmit client are be supported per MAC. A flag in the filter table can manually be set when making filter entries which is then used to determine the priority level when receiving packets. This determines which queue to use. -A dedicated API is provided to send and receive the HP packets. - -The transmit HP queue is optionally rate limited using the Credit Based Shaper which is described below. Together, these features provide the machinery required by IEEE 802.1Qav, allowing reliable, low-latency delivery of time-sensitive streams over Ethernet networks. +The transmit HP queue is optionally rate limited using the Credit Based Shaper which is described below. Together, these features provide the mechanisms required by IEEE 802.1Qav enabling reliable, low-latency delivery of time-sensitive streams over Ethernet networks. Credit Based Shaper =================== -The Credit Based Shaper (CBS) uses the following mechanisms to manage egress rate: +The Credit Based Shaper (CBS), in conjunction with the HP queue, limits the bandwidth of non-time-sensitive traffic and ensures a reserved bandwidth for high-priority streams. + - * Credits: Each port or queue is assigned a "credit" that increases or decreases over time based on the network's traffic conditions. - * Idle Slope: Determines how quickly credits increase when the queue is idle (i.e., waiting to transmit). - * Send Slope: Determines how quickly credits decrease when the queue is actively transmitting. - * Bandwidth Limitation: CBS limits the bandwidth of non-time-sensitive traffic, ensuring reserved bandwidth for high-priority streams. +The CBS uses the following mechanisms to manage egress rate: + * Credits: The high priority queue is assigned a "credit" that increases or decreases over time based on the network's traffic conditions. + * Idle Slope: Determines how quickly credit increases when the queue is idle (i.e., waiting to transmit). + * Transmission of data decreases credit proportionally to the number of bits sent. -If the credit is positive, the traffic stream is eligible for transmission. If the credit is negative, the traffic stream is paused until the credit returns to a positive state. By spreading traffic out evenly over time using a CBS, the queue size in each bridge and endpoint can be shorter, which in turn reduces the latency experienced by traffic as it flows through the system. +If the credit is positive, the high priority stream is eligible for transmission and will always be transmitted before any low priority traffic. If the credit is negative, the high priority stream is paused until the credit returns to a positive state. By spreading traffic out evenly over time using a CBS, the queue size in each bridge and endpoint can be shorter, which in turn reduces the latency experienced by traffic as it flows through the system. -The RT MACs are passed an enum when instantiated allowing enabling or disabling of the CBS. In addition the MAC provides an API which adjusts the high-priority TX queue's credit based shaper's idle slope dynamically. +The RT MACs are passed an enum argument when instantiated which enables or disables the CBS. In addition the MAC provides an API which can adjust the high-priority transmit queue's CBS idle slope dynamically, for example, if a different bandwidth reservation is required. -The idle slope passed is a fractional value representing the number of bits per reference timer tick in a Q16.16 format defined by ``MII_CREDIT_FRACTIONAL_BITS`` allowing very precise control over bandwidth reservation. Please see the :ref:`api_section` for details and an example of how to convert from bits-per-second to the slope argument. +The idle slope passed is a fractional value representing the number of bits per reference timer tick in a Q16.16 format defined by ``MII_CREDIT_FRACTIONAL_BITS`` allowing very precise control over bandwidth reservation. Please see :ref:`api_section` for details and an example showing how to convert from bits-per-second to the slope argument. VLAN Tag Stripping ================== -In addition to standard MAC VLAN awareness of received packets when calculating payload length, the RT MAC also includes a feature to automatically strip VLAN tags. This is done inside the MAC so that the application can just treat the incoming packet as a standard Ethernet frame. VLAN stripping is dynamically controllable on a per-client basis. +In addition to standard MAC VLAN awareness of received packets when calculating payload length, the RT MAC also includes a feature to optionally strip VLAN tags. This is done inside the MAC so that the application can just treat the incoming packet as a standard Ethernet frame. VLAN stripping is dynamically controllable on a per-client basis. |newpage| From 2c8e8fafd39c5ba1f8e6fe8d53f762443575c775 Mon Sep 17 00:00:00 2001 From: Ed Date: Mon, 27 Jan 2025 15:21:13 +0000 Subject: [PATCH 11/22] More detail for standard API --- doc/rst/lib_ethernet.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/rst/lib_ethernet.rst b/doc/rst/lib_ethernet.rst index c7abdfb7..1d40d237 100644 --- a/doc/rst/lib_ethernet.rst +++ b/doc/rst/lib_ethernet.rst @@ -146,9 +146,11 @@ All MACs in this library support a number of useful features which can be config * Separately configurable Rx and Tx buffer sizes (queues). * VLAN aware packet reception. If the VLAN tag (0x8100) is seen the header length is automatically extended by 4 octets to support the Tag Protocol Identifier (TPID) and Tag Control Information (TCI). -Transmission of packets is via an API that blocks until the frame has been copied into the transmit queue. Reception of a packet can be a blocking call or combined with a notification allowing the client to ``select`` on the receive interface whereupon it can then receive the waiting packet. Please see the :ref:`api_section` for details on how to use the MAC. +Transmission of packets is via an API that blocks until the frame has been copied into the transmit queue. This means the buffer size should be appropriately sized for your application or the application should tolerate blocking. -In addition the RMII RT MAC supports an exit command. This tears down all tasks associated with the MAC and frees the memory and xcore resources used, including ports. This can be helpful in cases where ports may be shared (eg. Flash memory) allowing DFU support in package constrained systems. It may also be used to support multiple connect PHY devices where redundancy is required, without costing the chip resources to support multiple MACs. +Reception of a packet is a blocking call until a packet is available but may combined with an asynchronous notification allowing the client to ``select`` on the XC interface whereupon it can then receive the waiting packet. This provides an efficient, event-driven API option. Please see the :ref:`api_section` for details on how to use the MAC. + +In addition, the RMII RT MAC supports an exit command. This tears down all tasks associated with the MAC and frees the memory and XCORE resources used, including ports. After exit, the MAC may be re-started again. This can be helpful in cases where ports may be shared (eg. Flash memory) allowing DFU support in package constrained systems. It may also be used to support multiple connect PHY devices where redundancy is required, without costing the chip resources to support multiple MACs. |newpage| From 96f3b018bea59f215cc8e49a4a237f92148d6aeb Mon Sep 17 00:00:00 2001 From: Ed Date: Mon, 27 Jan 2025 17:50:26 +0000 Subject: [PATCH 12/22] Add MII_CREDIT_FRACTIONAL_BITS to API --- lib_ethernet/api/ethernet.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib_ethernet/api/ethernet.h b/lib_ethernet/api/ethernet.h index 44a5e418..87414f1a 100644 --- a/lib_ethernet/api/ethernet.h +++ b/lib_ethernet/api/ethernet.h @@ -9,10 +9,13 @@ #include "doxygen.h" // Sphynx Documentation Workarounds -#define ETHERNET_ALL_INTERFACES (-1) -#define ETHERNET_MAX_PACKET_SIZE (1518) /**< MAX packet size in bytes */ +#define ETHERNET_ALL_INTERFACES (-1) +#define ETHERNET_MAX_PACKET_SIZE (1518) /**< MAX packet size in bytes */ + +#define MACADDR_NUM_BYTES (6) /**< Number of octets in MAC address */ + +#define MII_CREDIT_FRACTIONAL_BITS (16) /** Fractional bits for QAV credit based shaper */ -#define MACADDR_NUM_BYTES 6 /** Type representing the type of packet from the MAC */ typedef enum eth_packet_type_t { @@ -77,7 +80,7 @@ typedef interface ethernet_cfg_if { * \param ifnum The index of the MAC interface to set * \param mac_address The six-octet MAC address to set */ - void set_macaddr(size_t ifnum, uint8_t mac_address[MACADDR_NUM_BYTES]); + void set_macaddr(size_t ifnum, const uint8_t mac_address[MACADDR_NUM_BYTES]); /** Gets the source MAC address of the Ethernet MAC * From 83a9ccab412febf04a832ce0b9f77a0db92ee6ae Mon Sep 17 00:00:00 2001 From: Ed Date: Mon, 27 Jan 2025 17:51:19 +0000 Subject: [PATCH 13/22] set_macaddr is const addr --- lib_ethernet/src/mii_ethernet_mac.xc | 2 +- lib_ethernet/src/mii_ethernet_rt_mac.xc | 2 +- lib_ethernet/src/rgmii_buffering.xc | 2 +- lib_ethernet/src/server_state.h | 2 -- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib_ethernet/src/mii_ethernet_mac.xc b/lib_ethernet/src/mii_ethernet_mac.xc index 837afe05..6ac1604b 100644 --- a/lib_ethernet/src/mii_ethernet_mac.xc +++ b/lib_ethernet/src/mii_ethernet_mac.xc @@ -160,7 +160,7 @@ static void mii_ethernet_aux(client mii_if i_mii, memcpy(r_mac_address, mac_address, sizeof mac_address); break; - case i_cfg[int i].set_macaddr(size_t ifnum, uint8_t r_mac_address[MACADDR_NUM_BYTES]): + case i_cfg[int i].set_macaddr(size_t ifnum, const uint8_t r_mac_address[MACADDR_NUM_BYTES]): memcpy(mac_address, r_mac_address, sizeof r_mac_address); break; diff --git a/lib_ethernet/src/mii_ethernet_rt_mac.xc b/lib_ethernet/src/mii_ethernet_rt_mac.xc index 6b81be4f..a4dd1b30 100644 --- a/lib_ethernet/src/mii_ethernet_rt_mac.xc +++ b/lib_ethernet/src/mii_ethernet_rt_mac.xc @@ -322,7 +322,7 @@ unsafe void mii_ethernet_server(mii_mempool_t rx_mem, memcpy(r_mac_address, mac_address, sizeof mac_address); break; - case i_cfg[int i].set_macaddr(size_t ifnum, uint8_t r_mac_address[MACADDR_NUM_BYTES]): + case i_cfg[int i].set_macaddr(size_t ifnum, const uint8_t r_mac_address[MACADDR_NUM_BYTES]): memcpy(mac_address, r_mac_address, sizeof r_mac_address); break; diff --git a/lib_ethernet/src/rgmii_buffering.xc b/lib_ethernet/src/rgmii_buffering.xc index fc26c2cc..1c33f89f 100644 --- a/lib_ethernet/src/rgmii_buffering.xc +++ b/lib_ethernet/src/rgmii_buffering.xc @@ -747,7 +747,7 @@ void rgmii_ethernet_mac_config(server ethernet_cfg_if i_cfg[n], memcpy(r_mac_address, mac_address, sizeof mac_address); break; - case i_cfg[int i].set_macaddr(size_t ifnum, uint8_t r_mac_address[MACADDR_NUM_BYTES]): + case i_cfg[int i].set_macaddr(size_t ifnum, const uint8_t r_mac_address[MACADDR_NUM_BYTES]): memcpy(mac_address, r_mac_address, sizeof r_mac_address); break; diff --git a/lib_ethernet/src/server_state.h b/lib_ethernet/src/server_state.h index c0c25dd0..7d84e99f 100644 --- a/lib_ethernet/src/server_state.h +++ b/lib_ethernet/src/server_state.h @@ -6,8 +6,6 @@ #include "xccompat.h" #include "ethernet.h" -#define MII_CREDIT_FRACTIONAL_BITS 16 - // Server is shared for rmii/mii so pass in enum typedef enum phy_100mb_t { ETH_MAC_IF_MII = 0, From 5b7a2a2195be898c0a4b4f2edba57186d84acf2e Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 28 Jan 2025 13:33:08 +0000 Subject: [PATCH 14/22] Initial move shaper to helper file --- lib_ethernet/src/mii_master.xc | 28 +++------ lib_ethernet/src/shaper.h | 102 +++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 lib_ethernet/src/shaper.h diff --git a/lib_ethernet/src/mii_master.xc b/lib_ethernet/src/mii_master.xc index 4be33d80..10b6283d 100644 --- a/lib_ethernet/src/mii_master.xc +++ b/lib_ethernet/src/mii_master.xc @@ -13,6 +13,7 @@ #include "mii_common_lld.h" #include "string.h" #include "check_ifg_wait.h" +#include "shaper.h" #define QUOTEAUX(x) #x #define QUOTE(x) QUOTEAUX(x) @@ -473,6 +474,7 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, { int credit = 0; // No. of bits allowed to send int credit_time; + int prev_credit_time; // Need one timer to be able to read at any time for the shaper timer credit_tmr; // And a second timer to be enforcing the IFG gap @@ -486,6 +488,7 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, if (ETHERNET_SUPPORT_HP_QUEUES && enable_shaper) { credit_tmr :> credit_time; + prev_credit_time = credit_time; } ifg_tmr :> ifg_time; @@ -500,21 +503,8 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, buf = mii_get_next_buf(packets_hp); if (enable_shaper) { - int prev_credit_time = credit_time; credit_tmr :> credit_time; - - int elapsed = credit_time - prev_credit_time; - credit += elapsed * p_port_state->qav_idle_slope; // add bit budget since last transmission to credit. ticks * bits/tick = bits - - if (buf) { - if (credit < 0) { - buf = 0; // if out of credit drop this HP packet - } - } - else { - if (credit > 0) - credit = 0; - } + buf = shaper_do_idle_slope(buf, credit_time, prev_credit_time, credit, p_port_state->qav_idle_slope); } if (!buf) { @@ -530,19 +520,15 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, unsigned time = mii_transmit_packet(tx_mem, buf, p_mii_txd, ifg_tmr, ifg_time, eof_time); - eof_time = ifg_time; // Setup the hardware timer to enforce the IFG + eof_time = ifg_time; ifg_time += MII_ETHERNET_IFS_AS_REF_CLOCK_COUNT; ifg_time += (buf->length & 0x3) * 8; + // Calculate the send slope (decrement credit) if enabled and was HP const int packet_is_high_priority = (p_ts_queue == null); if (enable_shaper && packet_is_high_priority) { - const int preamble_bytes = 8; - const int ifg_bytes = 96/8; - const int crc_bytes = 4; - int len = buf->length + preamble_bytes + ifg_bytes + crc_bytes; - // decrease credit by no. of bits transmitted - credit = credit - (len << (MII_CREDIT_FRACTIONAL_BITS+3)); // MII_CREDIT_FRACTIONAL_BITS+3 to convert from bytes to bits + shaper_do_send_slope(buf->length, credit); } if (mii_get_and_dec_transmit_count(buf) == 0) { diff --git a/lib_ethernet/src/shaper.h b/lib_ethernet/src/shaper.h new file mode 100644 index 00000000..de5bdd84 --- /dev/null +++ b/lib_ethernet/src/shaper.h @@ -0,0 +1,102 @@ +// Copyright 2025 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef __shaper_h__ +#define __shaper_h__ + +#include "ethernet.h" + +/** Check if IFG time wait is required before starting a transmission + * + * This function ensures that the IFG wait happens only when the current timestamp is, + * - in between the last packet end time and next packet start time, provided the next packet start time has not wrapped around + * when compared to the last packet end time, + * - or, less than the next packet start time if the next packet start time has wrapped around wrt last packet end time + * + * This ensures that the transmitter doesn't end up doing arbitrarily long waits (~42s in the worst case) before transmitting + * the next packet. In the worst case, an extra IFG time wait would happen. + * This is required to handle cases where there's a gap between transmissions and the timer wraps around + * + * + * \param last_frame_end_time Ref timer timestamp corresponsing to when the last frame was transmitted + * \param next_frame_start_time Ref timer timestamp corresponsing to when the next frame needs to be + * transmitted. This is equal to the last frame time + the IFG gap + * \param current_time Ref timer timestamp for the current time + * + * \returns A flag indicating whether the IFG wait is required. 0: No wait, 1: Wait. + * + * + */ +inline unsigned check_if_ifg_wait_requiredfffff( + unsigned last_frame_end_time, + unsigned next_frame_start_time, + unsigned current_time) +{ + if(next_frame_start_time > last_frame_end_time) // next_frame_start_time hasn't wrapped around + { + // Only wait when current time is in between last frame end and next frame start time + if((current_time > last_frame_end_time) && (current_time < next_frame_start_time)) + { + return 1; + } + } + else // next_frame_start_time has already wrapped around + { + // only wait when current_time is less than next_frame_start_time + if(current_time < next_frame_start_time) + { + return 1; + } + } + return 0; +} + +static unsigned idle_slope_bps_calc(unsigned bits_per_second){ + unsigned long long slope = ((unsigned long long) bits_per_second) << (MII_CREDIT_FRACTIONAL_BITS); + slope = slope / XS1_TIMER_HZ; // bits that should be sent per ref timer tick + + return (unsigned) slope; +} + + + +static inline mii_packet_t * unsafe shaper_do_idle_slope(mii_packet_t * unsafe buf, + int current_time, + int &prev_time, + int &credit, + unsigned qav_idle_slope){ + int elapsed = current_time - prev_time; + credit += elapsed * (int)qav_idle_slope; // add bit budget since last transmission to credit. ticks * bits/tick = bits + + // If valid hp buffer + if (buf) { + if (credit < 0) { + buf = 0; // if out of credit drop this HP packet + } + } + else + // Buffer invalid, no HP packet so reset credit to zero so we don't end up with huge burst + { + if (credit > 0){ + credit = 0; + } + } + // Update record of last time + prev_time = current_time; + + return buf; +} + +static inline void shaper_do_send_slope(unsigned len_bytes, int &credit){ + // Calculate number of additional byte slots on wire over the payload + const unsigned preamble_bytes = 8; + const unsigned ifg_bytes = 96 / 8; + const unsigned crc_bytes = 4; + + len_bytes += preamble_bytes + ifg_bytes + crc_bytes; + + // decrease credit by no. of bits transmitted, scaled by MII_CREDIT_FRACTIONAL_BITS + credit = credit - (len_bytes << (MII_CREDIT_FRACTIONAL_BITS + 3)); // MII_CREDIT_FRACTIONAL_BITS+3 to convert from bytes to bits +} + + +#endif // __shaper_h__ From b5adfebcc8e5646af72d44336a212d7c2c146d45 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 28 Jan 2025 13:33:42 +0000 Subject: [PATCH 15/22] Initial test WIP --- tests/CMakeLists.txt | 1 + tests/test_do_idle_slope.py | 22 +++++++ tests/test_do_idle_slope/CMakeLists.txt | 21 ++++++ tests/test_do_idle_slope/src/main.xc | 86 +++++++++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 tests/test_do_idle_slope.py create mode 100644 tests/test_do_idle_slope/CMakeLists.txt create mode 100644 tests/test_do_idle_slope/src/main.xc diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d51f1f4c..30394b09 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,4 +21,5 @@ add_subdirectory(test_check_ifg_wait) add_subdirectory(test_rmii_timing) add_subdirectory(test_rmii_restart) add_subdirectory(test_smi) +add_subdirectory(test_do_idle_slope) diff --git a/tests/test_do_idle_slope.py b/tests/test_do_idle_slope.py new file mode 100644 index 00000000..739565e9 --- /dev/null +++ b/tests/test_do_idle_slope.py @@ -0,0 +1,22 @@ +# Copyright 2025 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +# +import os +import sys +from pathlib import Path +import pytest +import Pyxsim as px + +pkg_dir = Path(__file__).parent +def test_do_idle_slope(capfd): + testname = 'test_do_idle_slope' + binary = pkg_dir / testname / 'bin' / f'{testname}.xe' + assert os.path.isfile(binary) + expect_filename = pkg_dir / f'{testname}.expect' + tester = px.testers.ComparisonTester(open(expect_filename)) + result = px.run_on_simulator_( binary, + tester=tester, + do_xe_prebuild=False, + capfd=capfd) + + assert result is True, f"{result}" diff --git a/tests/test_do_idle_slope/CMakeLists.txt b/tests/test_do_idle_slope/CMakeLists.txt new file mode 100644 index 00000000..313a86da --- /dev/null +++ b/tests/test_do_idle_slope/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.21) +include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake) +project(test_do_idle_slope) + +set(APP_PCA_ENABLE ON) +include(../test_deps.cmake) + +set(APP_HW_TARGET XCORE-AI-EXPLORER) + +set(APP_PCA_ENABLE ON) + +set(COMPILER_FLAGS_COMMON -g + -report + -DDEBUG_PRINT_ENABLE + -Os) + +set(APP_COMPILER_FLAGS ${COMPILER_FLAGS_COMMON}) + +set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) + +XMOS_REGISTER_APP() diff --git a/tests/test_do_idle_slope/src/main.xc b/tests/test_do_idle_slope/src/main.xc new file mode 100644 index 00000000..eebb7985 --- /dev/null +++ b/tests/test_do_idle_slope/src/main.xc @@ -0,0 +1,86 @@ +// Copyright 2025 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#include +#include +#include +#include "debug_print.h" +#include +#include "ethernet.h" +#include "mii_buffering.h" +#include "shaper.h" + +/* +inline mii_packet_t * unsafe shaper_do_idle_slope(mii_packet_t * unsafe buf, + int curr_time, + int &prev_time, + int &credit, + unsigned qav_idle_slope +*/ + +int main() +{ + unsafe{ + mii_packet_t mii_packet; + mii_packet_t * unsafe buff = &mii_packet; + int result = 0; + + // GROUP1: last_frame_time is not wrapped around wrt next_frame_time + int last_frame_time = 0xe0000000; + int next_frame_time = 0xf0000000; + int credit = 0; + unsigned qav_idle_slope = idle_slope_bps_calc(75000000); // Set to max reservation to test for overflows + + // case 1: now is somewhere between last and next + buff = shaper_do_idle_slope(buff, last_frame_time, next_frame_time, credit, qav_idle_slope); + if(!buff) + { + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff == 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + result = -1; + } + buff = &mii_packet; + + // case2: now is outside the window and more than next_frame_time + buff = shaper_do_idle_slope(buff, last_frame_time, next_frame_time, credit, qav_idle_slope); + if(!buff) + { + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff == 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + result = -1; + } + buff = &mii_packet; + + // case3: now is outside the window and wrapped around + buff = shaper_do_idle_slope(buff, last_frame_time, next_frame_time, credit, qav_idle_slope); + if(!buff) + { + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff == 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + result = -1; + } + buff = &mii_packet; + + // GROUP2: next_frame_time has wrapped around wrt last_frame_time + last_frame_time = 0xffff0000; + next_frame_time = 0x10000000; + + // case4: now is before next_frame_time + buff = shaper_do_idle_slope(buff, last_frame_time, next_frame_time, credit, qav_idle_slope); + if(!buff) + { + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff == 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + result = -1; + } + buff = &mii_packet; + + // case5: now is after next_frame_time + buff = shaper_do_idle_slope(buff, last_frame_time, next_frame_time, credit, qav_idle_slope); + if(!buff) + { + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff == 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + result = -1; + } + buff = &mii_packet; + + debug_printf("%s\n", result == 0 ? "PASS" : "FAIL"); + return result; + } +} From 6c05cf5b2ee12104ec20c8b7420e5a5c9ee6510d Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 28 Jan 2025 17:32:39 +0000 Subject: [PATCH 16/22] Update shaper_do_idle_slope so it's ASM compatible. Failing test included. --- lib_ethernet/src/mii_master.xc | 4 +- lib_ethernet/src/shaper.h | 20 +++--- tests/test_do_idle_slope/src/main.xc | 101 ++++++++++++++++++++------- 3 files changed, 88 insertions(+), 37 deletions(-) diff --git a/lib_ethernet/src/mii_master.xc b/lib_ethernet/src/mii_master.xc index 10b6283d..154c788f 100644 --- a/lib_ethernet/src/mii_master.xc +++ b/lib_ethernet/src/mii_master.xc @@ -504,9 +504,9 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, if (enable_shaper) { credit_tmr :> credit_time; - buf = shaper_do_idle_slope(buf, credit_time, prev_credit_time, credit, p_port_state->qav_idle_slope); + {buf, prev_credit_time, credit} = shaper_do_idle_slope(buf, prev_credit_time, credit, credit_time, p_port_state->qav_idle_slope); } - + if (!buf) { buf = mii_get_next_buf(packets_lp); p_ts_queue = &ts_queue; diff --git a/lib_ethernet/src/shaper.h b/lib_ethernet/src/shaper.h index de5bdd84..1e935251 100644 --- a/lib_ethernet/src/shaper.h +++ b/lib_ethernet/src/shaper.h @@ -59,18 +59,18 @@ static unsigned idle_slope_bps_calc(unsigned bits_per_second){ -static inline mii_packet_t * unsafe shaper_do_idle_slope(mii_packet_t * unsafe buf, - int current_time, - int &prev_time, - int &credit, - unsigned qav_idle_slope){ +{mii_packet_t * unsafe, int, int} static inline shaper_do_idle_slope(mii_packet_t * unsafe hp_buf, + int prev_time, + int credit, + int current_time, + unsigned qav_idle_slope){ int elapsed = current_time - prev_time; credit += elapsed * (int)qav_idle_slope; // add bit budget since last transmission to credit. ticks * bits/tick = bits // If valid hp buffer - if (buf) { + if (hp_buf) { if (credit < 0) { - buf = 0; // if out of credit drop this HP packet + hp_buf = 0; // if out of credit drop this HP packet } } else @@ -80,10 +80,10 @@ static inline mii_packet_t * unsafe shaper_do_idle_slope(mii_packet_t * unsafe b credit = 0; } } - // Update record of last time - prev_time = current_time; - return buf; + // just for readibilty + int previous_time = current_time; + return {hp_buf, previous_time, credit}; } static inline void shaper_do_send_slope(unsigned len_bytes, int &credit){ diff --git a/tests/test_do_idle_slope/src/main.xc b/tests/test_do_idle_slope/src/main.xc index eebb7985..cf307280 100644 --- a/tests/test_do_idle_slope/src/main.xc +++ b/tests/test_do_idle_slope/src/main.xc @@ -11,11 +11,13 @@ #include "shaper.h" /* -inline mii_packet_t * unsafe shaper_do_idle_slope(mii_packet_t * unsafe buf, - int curr_time, - int &prev_time, - int &credit, - unsigned qav_idle_slope +{mii_packet_t * unsafe, int} static inline shaper_do_idle_slope(mii_packet_t * unsafe hp_buf, + int prev_time, + int credit, + int current_time, + unsigned qav_idle_slope){ +return {hp_buf, previous_time, credit}; + */ int main() @@ -26,56 +28,105 @@ int main() int result = 0; // GROUP1: last_frame_time is not wrapped around wrt next_frame_time - int last_frame_time = 0xe0000000; - int next_frame_time = 0xf0000000; + int last_copy = 0; int credit = 0; unsigned qav_idle_slope = idle_slope_bps_calc(75000000); // Set to max reservation to test for overflows + debug_printf("Idle_slope: %u\n", qav_idle_slope); + int num_ticks_to_overflow = 0x7fffffff / qav_idle_slope; + debug_printf("num_ticks_to_overflow: %d\n", num_ticks_to_overflow); + - // case 1: now is somewhere between last and next - buff = shaper_do_idle_slope(buff, last_frame_time, next_frame_time, credit, qav_idle_slope); + // case 1: Small increment + int last_frame_time = 0x00000000; + int next_frame_time = 0x00001000; + {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); if(!buff) { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff == 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); result = -1; } buff = &mii_packet; - // case2: now is outside the window and more than next_frame_time - buff = shaper_do_idle_slope(buff, last_frame_time, next_frame_time, credit, qav_idle_slope); + // case2: Small increment across halfway though + credit = 0; + last_frame_time = 0x7ffff800; + next_frame_time = 0x80000800; + {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); if(!buff) { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff == 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); result = -1; } buff = &mii_packet; - // case3: now is outside the window and wrapped around - buff = shaper_do_idle_slope(buff, last_frame_time, next_frame_time, credit, qav_idle_slope); + // case3: Small increment over halfway though + credit = 0; + last_frame_time = 0x90000000; + next_frame_time = 0x90001000; + {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); if(!buff) { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff == 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + result = -1; + } + buff = &mii_packet; + + // case4: Reset and do big increment, will definitely overflow credit + credit = 0; + last_frame_time = 0x10000000; + next_frame_time = 0x1000b000; + {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); + if(!buff) + { + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); result = -1; } buff = &mii_packet; - // GROUP2: next_frame_time has wrapped around wrt last_frame_time - last_frame_time = 0xffff0000; - next_frame_time = 0x10000000; + // case5: Close to max + credit = 0; + last_frame_time = 0; + next_frame_time = num_ticks_to_overflow - 10; + {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); + if(!buff) + { + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + result = -1; + } + buff = &mii_packet; + + // case6: Push case5 past overflow + last_frame_time = 0; + next_frame_time = 20; + {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); + if(!buff) + { + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + result = -1; + } + buff = &mii_packet; - // case4: now is before next_frame_time - buff = shaper_do_idle_slope(buff, last_frame_time, next_frame_time, credit, qav_idle_slope); + // GROUP2: next_frame_time has wrapped around wrt last_frame_time + // case7: now is before next_frame_time + credit = 0; + last_frame_time = 0xfffff800; + next_frame_time = 0x00000800; + {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); if(!buff) { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff == 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); result = -1; } buff = &mii_packet; - // case5: now is after next_frame_time - buff = shaper_do_idle_slope(buff, last_frame_time, next_frame_time, credit, qav_idle_slope); + // case8: wrapping but REALLY big + credit = 0; + last_frame_time = 0xffffffff; + next_frame_time = 0xfffffffe; + {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); if(!buff) { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff == 0 ? "True" : "False", last_frame_time, next_frame_time, credit); + debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); result = -1; } buff = &mii_packet; From 4fc208de1ba47d37680b9d7b17e648266c3bf969 Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 29 Jan 2025 15:11:30 +0000 Subject: [PATCH 17/22] Major re-work of shaper --- lib_ethernet/api/ethernet.h | 4 +- lib_ethernet/src/mii_master.xc | 19 ++- lib_ethernet/src/server_state.h | 1 + lib_ethernet/src/shaper.h | 167 +++++++++++++++------------ lib_ethernet/src/shaper.xc | 22 ++++ tests/test_do_idle_slope.expect | 0 tests/test_do_idle_slope/src/main.xc | 148 ++++++++++-------------- 7 files changed, 185 insertions(+), 176 deletions(-) create mode 100644 lib_ethernet/src/shaper.xc create mode 100644 tests/test_do_idle_slope.expect diff --git a/lib_ethernet/api/ethernet.h b/lib_ethernet/api/ethernet.h index 3cd1045e..6fdaff56 100644 --- a/lib_ethernet/api/ethernet.h +++ b/lib_ethernet/api/ethernet.h @@ -10,11 +10,11 @@ #define ETHERNET_ALL_INTERFACES (-1) -#define ETHERNET_MAX_PACKET_SIZE (1518) /**< MAX packet size in bytes */ +#define ETHERNET_MAX_PACKET_SIZE (1518) /**< MAX packet size in bytes including src, dst, ether/tags but NOT preamble or CRC*/ #define MACADDR_NUM_BYTES (6) /**< Number of octets in MAC address */ -#define MII_CREDIT_FRACTIONAL_BITS (16) /** Fractional bits for QAV credit based shaper */ +#define MII_CREDIT_FRACTIONAL_BITS (16) /** Fractional bits for Qav credit based shaper setting */ /** Type representing the type of packet from the MAC */ diff --git a/lib_ethernet/src/mii_master.xc b/lib_ethernet/src/mii_master.xc index 154c788f..b4aebf77 100644 --- a/lib_ethernet/src/mii_master.xc +++ b/lib_ethernet/src/mii_master.xc @@ -472,10 +472,9 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, out buffered port:32 p_mii_txd, volatile ethernet_port_state_t * unsafe p_port_state) { - int credit = 0; // No. of bits allowed to send - int credit_time; - int prev_credit_time; - // Need one timer to be able to read at any time for the shaper + qav_state_t qav_state = {0, 0, 1}; // Set times to zero and credit to 1 so it can tx first frame + + // Need a timer to be able to read at any time for the shaper timer credit_tmr; // And a second timer to be enforcing the IFG gap hwtimer_t ifg_tmr; @@ -487,8 +486,8 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, enable_shaper = 0; if (ETHERNET_SUPPORT_HP_QUEUES && enable_shaper) { - credit_tmr :> credit_time; - prev_credit_time = credit_time; + credit_tmr :> qav_state.current_time; + qav_state.prev_time = qav_state.current_time;; } ifg_tmr :> ifg_time; @@ -503,8 +502,8 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, buf = mii_get_next_buf(packets_hp); if (enable_shaper) { - credit_tmr :> credit_time; - {buf, prev_credit_time, credit} = shaper_do_idle_slope(buf, prev_credit_time, credit, credit_time, p_port_state->qav_idle_slope); + credit_tmr :> qav_state.current_time; + buf = shaper_do_idle_slope(buf, &qav_state, p_port_state); } if (!buf) { @@ -528,7 +527,7 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, // Calculate the send slope (decrement credit) if enabled and was HP const int packet_is_high_priority = (p_ts_queue == null); if (enable_shaper && packet_is_high_priority) { - shaper_do_send_slope(buf->length, credit); + shaper_do_send_slope(buf->length, &qav_state); } if (mii_get_and_dec_transmit_count(buf) == 0) { @@ -545,6 +544,6 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, mii_free_current(packets_hp); } } - } + } // while(1) } diff --git a/lib_ethernet/src/server_state.h b/lib_ethernet/src/server_state.h index 7d84e99f..cf5d62e3 100644 --- a/lib_ethernet/src/server_state.h +++ b/lib_ethernet/src/server_state.h @@ -19,6 +19,7 @@ typedef struct ethernet_port_state_t ethernet_speed_t link_speed; int qav_shaper_enabled; int qav_idle_slope; + int64_t qav_credit_limit; int ingress_ts_latency[NUM_ETHERNET_SPEEDS]; int egress_ts_latency[NUM_ETHERNET_SPEEDS]; } ethernet_port_state_t; diff --git a/lib_ethernet/src/shaper.h b/lib_ethernet/src/shaper.h index 1e935251..7088cf62 100644 --- a/lib_ethernet/src/shaper.h +++ b/lib_ethernet/src/shaper.h @@ -3,99 +3,112 @@ #ifndef __shaper_h__ #define __shaper_h__ +#include #include "ethernet.h" +#include "server_state.h" +#include "mii_buffering.h" +#include "xassert.h" +#include -/** Check if IFG time wait is required before starting a transmission - * - * This function ensures that the IFG wait happens only when the current timestamp is, - * - in between the last packet end time and next packet start time, provided the next packet start time has not wrapped around - * when compared to the last packet end time, - * - or, less than the next packet start time if the next packet start time has wrapped around wrt last packet end time - * - * This ensures that the transmitter doesn't end up doing arbitrarily long waits (~42s in the worst case) before transmitting - * the next packet. In the worst case, an extra IFG time wait would happen. - * This is required to handle cases where there's a gap between transmissions and the timer wraps around - * +#ifndef ETHERNET_SUPPORT_TRAFFIC_SHAPER_CREDIT_LIMIT +#define ETHERNET_SUPPORT_TRAFFIC_SHAPER_CREDIT_LIMIT 1 +#endif + +static const unsigned preamble_bytes = 8; +static const unsigned crc_bytes = 4; +static const unsigned ifg_bytes = 96 / 8; + +/** Type which stores the Qav credit based shaper state */ +typedef struct qav_state_t{ + int prev_time; /**< Previous time in hw_timer ticks (10ns, 32b). */ + int current_time; /**< Current time in hw_timer ticks (10ns, 32b). */ + int credit; /**< Credit in MII_CREDIT_FRACTIONAL_BITS fractional format. */ +} qav_state_t; + +/** Sets the Qav idle slope in units of bits per second. * - * \param last_frame_end_time Ref timer timestamp corresponsing to when the last frame was transmitted - * \param next_frame_start_time Ref timer timestamp corresponsing to when the next frame needs to be - * transmitted. This is equal to the last frame time + the IFG gap - * \param current_time Ref timer timestamp for the current time + * \param port_state Pointer to the port state to be modified + * \param limit_bps The idle slope setting in bits per seconds * - * \returns A flag indicating whether the IFG wait is required. 0: No wait, 1: Wait. + */ +void set_qav_idle_slope(ethernet_port_state_t * port_state, unsigned limit_bps); + + +/** Sets the Qav credit limit in units of frame size byte * + * \param port_state Pointer to the port state to be modified + * \param limit_bytes The credit limit in units of payload size in bytes to set as a credit limit, + * not including preamble, CRC and IFG. Set to -1 for no limit (default) * */ -inline unsigned check_if_ifg_wait_requiredfffff( - unsigned last_frame_end_time, - unsigned next_frame_start_time, - unsigned current_time) -{ - if(next_frame_start_time > last_frame_end_time) // next_frame_start_time hasn't wrapped around - { - // Only wait when current time is in between last frame end and next frame start time - if((current_time > last_frame_end_time) && (current_time < next_frame_start_time)) - { - return 1; - } - } - else // next_frame_start_time has already wrapped around - { - // only wait when current_time is less than next_frame_start_time - if(current_time < next_frame_start_time) - { - return 1; - } - } - return 0; -} - -static unsigned idle_slope_bps_calc(unsigned bits_per_second){ - unsigned long long slope = ((unsigned long long) bits_per_second) << (MII_CREDIT_FRACTIONAL_BITS); - slope = slope / XS1_TIMER_HZ; // bits that should be sent per ref timer tick - - return (unsigned) slope; -} +void set_qav_credit_limit(ethernet_port_state_t * port_state, int payload_limit_bytes); +/** Performs the idle slope calculation. + * + * Adds credits based on time since last HP packet and bit rate. If credit after calculation is above zero then + * the HP packet is allowed to be transmitted. If credit is zero or less then the return buffer + * is null so that it waits until credit is sufficient. + * + * The credit is optionally limited to hiCredit which is initialised by set_qav_credit_limit() + * + * \param hp_buf Pointer to packet to be transmitted + * \param qav_state Pointer to Qav state struct + * \param port_state Pointer to MAC port state + * + * \returns The hp_buf passed to it which is either maintained if credit is sufficient + * or NULL if credit is not sufficient. + */ +static inline mii_packet_t * unsafe shaper_do_idle_slope(mii_packet_t * unsafe hp_buf, + qav_state_t * unsafe qav_state, + ethernet_port_state_t * unsafe port_state){ + unsafe{ + uint32_t elapsed_ticks = qav_state->current_time - qav_state->prev_time; -{mii_packet_t * unsafe, int, int} static inline shaper_do_idle_slope(mii_packet_t * unsafe hp_buf, - int prev_time, - int credit, - int current_time, - unsigned qav_idle_slope){ - int elapsed = current_time - prev_time; - credit += elapsed * (int)qav_idle_slope; // add bit budget since last transmission to credit. ticks * bits/tick = bits +#if ETHERNET_SUPPORT_TRAFFIC_SHAPER_CREDIT_LIMIT + int64_t credit64 = (int64_t)elapsed_ticks * (int64_t)port_state->qav_idle_slope + (int64_t)qav_state->credit; + if(credit64 > port_state->qav_credit_limit) + { + qav_state->credit = port_state->qav_credit_limit; + } else { + qav_state->credit += (int)credit64; + } +#else + qav_state->credit += elapsed_ticks * (int)port_state->qav_idle_slope; // add bit budget since last transmission to credit. ticks * bits/tick = bits +#endif - // If valid hp buffer - if (hp_buf) { - if (credit < 0) { - hp_buf = 0; // if out of credit drop this HP packet + // If valid hp buffer + if (hp_buf) { + if (qav_state->credit < 0) { + hp_buf = 0; // if out of credit drop this HP packet + } } - } - else - // Buffer invalid, no HP packet so reset credit to zero so we don't end up with huge burst - { - if (credit > 0){ - credit = 0; + else + // Buffer invalid, no HP packet so reset credit as per Annex L of Qav + { + if (qav_state->credit > 0){ + qav_state->credit = 0; + } } - } - // just for readibilty - int previous_time = current_time; - return {hp_buf, previous_time, credit}; -} + // Ensure we keep track of state (time since last packet) for next time + qav_state->prev_time = qav_state->current_time; -static inline void shaper_do_send_slope(unsigned len_bytes, int &credit){ - // Calculate number of additional byte slots on wire over the payload - const unsigned preamble_bytes = 8; - const unsigned ifg_bytes = 96 / 8; - const unsigned crc_bytes = 4; + return hp_buf; + } +} - len_bytes += preamble_bytes + ifg_bytes + crc_bytes; - - // decrease credit by no. of bits transmitted, scaled by MII_CREDIT_FRACTIONAL_BITS - credit = credit - (len_bytes << (MII_CREDIT_FRACTIONAL_BITS + 3)); // MII_CREDIT_FRACTIONAL_BITS+3 to convert from bytes to bits +static inline void shaper_do_send_slope(int len_bytes, qav_state_t * unsafe qav_state){ + unsafe{ + // Calculate number of additional byte slots on wire over the payload + const int overhead_bytes = preamble_bytes + crc_bytes + ifg_bytes; + + // decrease credit by no. of bits transmitted, scaled by MII_CREDIT_FRACTIONAL_BITS + // Note we don't need to check for overflow here as we will only be here if credit + // was previously positive (worst case 1) and len_bytes <= ETHERNET_MAX_PACKET_SIZE so + // will only decrement by roughly -(1<<29) + qav_state->credit = qav_state->credit - ((len_bytes + overhead_bytes) << (MII_CREDIT_FRACTIONAL_BITS + 3)); // MII_CREDIT_FRACTIONAL_BITS+3 to convert from bytes to bits + } } diff --git a/lib_ethernet/src/shaper.xc b/lib_ethernet/src/shaper.xc new file mode 100644 index 00000000..7c53a8c6 --- /dev/null +++ b/lib_ethernet/src/shaper.xc @@ -0,0 +1,22 @@ +// Copyright 2025 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include "ethernet.h" +#include "shaper.h" + +void set_qav_idle_slope(ethernet_port_state_t * port_state, unsigned limit_bps) +{ + // Scale for 16.16 representation + uint64_t slope = ((uint64_t)limit_bps) << MII_CREDIT_FRACTIONAL_BITS; + + // Calculate bits per tick per bit in 16.16 + slope = slope / XS1_TIMER_HZ; + + port_state->qav_idle_slope = (unsigned)slope; +} + + +void set_qav_credit_limit(ethernet_port_state_t * port_state, int payload_limit_bytes) +{ + int64_t max_interferring_frame_bits = (preamble_bytes + payload_limit_bytes + crc_bytes + ifg_bytes) * 8; + port_state->qav_credit_limit = max_interferring_frame_bits; +} diff --git a/tests/test_do_idle_slope.expect b/tests/test_do_idle_slope.expect new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_do_idle_slope/src/main.xc b/tests/test_do_idle_slope/src/main.xc index cf307280..3cd1f11b 100644 --- a/tests/test_do_idle_slope/src/main.xc +++ b/tests/test_do_idle_slope/src/main.xc @@ -20,116 +20,90 @@ return {hp_buf, previous_time, credit}; */ -int main() -{ +int test_num = 1; + +int do_test(int last_time, int current_time, qav_state_t *qav_state, ethernet_port_state_t *port_state){ unsafe{ + int result = 0; + int t0; + int t1; + timer tmr; + mii_packet_t mii_packet; mii_packet_t * unsafe buff = &mii_packet; + + qav_state->prev_time = last_time; + qav_state->current_time = current_time; + tmr :> t0; + buff = shaper_do_idle_slope(buff, qav_state, port_state); + tmr :> t1; + debug_printf("Ticks: %d\n", t1-t0); + if(!buff) + { + debug_printf("Error test (%d): Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", test_num, buff != 0 ? "True" : "False", last_time, qav_state->current_time, qav_state->credit); + result = -1; + } + + test_num++; + return result; + } +} + +// To make volatile +in port p_vol = XS1_PORT_1A; + +int main(void){ + unsafe{ + ethernet_port_state_t port_state; + qav_state_t qav_state = {0, 0, 0}; + int result = 0; + int vol, vol2; + p_vol :> vol; // This will be zero. We add these to force the compiler to call shaper_do_idle_slope fpor the timing analysis + p_vol :> vol2;// otherwise the const expressions get optimised away + // GROUP1: last_frame_time is not wrapped around wrt next_frame_time int last_copy = 0; - int credit = 0; - unsigned qav_idle_slope = idle_slope_bps_calc(75000000); // Set to max reservation to test for overflows - debug_printf("Idle_slope: %u\n", qav_idle_slope); - int num_ticks_to_overflow = 0x7fffffff / qav_idle_slope; - debug_printf("num_ticks_to_overflow: %d\n", num_ticks_to_overflow); + port_state.link_speed = LINK_100_MBPS_FULL_DUPLEX; + set_qav_idle_slope(&port_state, 75000000); // Set to max reservation to test for overflows + set_qav_credit_limit(&port_state, ETHERNET_MAX_PACKET_SIZE); + + debug_printf("Idle_slope: %u\n", port_state.qav_idle_slope); + debug_printf("credit_limit: %d\n", port_state.qav_credit_limit); // case 1: Small increment - int last_frame_time = 0x00000000; - int next_frame_time = 0x00001000; - {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); - if(!buff) - { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); - result = -1; - } - buff = &mii_packet; + result -= do_test(0x00000000 + vol, 0x00000100 + vol2, &qav_state, &port_state); + // case2: Small increment across halfway though - credit = 0; - last_frame_time = 0x7ffff800; - next_frame_time = 0x80000800; - {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); - if(!buff) - { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); - result = -1; - } - buff = &mii_packet; + qav_state.credit = 0; + result -= do_test(0x7ffff800 + vol, 0x80000800 + vol2, &qav_state, &port_state); // case3: Small increment over halfway though - credit = 0; - last_frame_time = 0x90000000; - next_frame_time = 0x90001000; - {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); - if(!buff) - { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); - result = -1; - } - buff = &mii_packet; + qav_state.credit = 0; + result -= do_test(0x90000000 + vol, 0x90001000 + vol2, &qav_state, &port_state); // case4: Reset and do big increment, will definitely overflow credit - credit = 0; - last_frame_time = 0x10000000; - next_frame_time = 0x1000b000; - {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); - if(!buff) - { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); - result = -1; - } - buff = &mii_packet; - + qav_state.credit = 0; + result -= do_test(0x00000000 + vol, 0x90000000 + vol2, &qav_state, &port_state); + // case5: Close to max - credit = 0; - last_frame_time = 0; - next_frame_time = num_ticks_to_overflow - 10; - {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); - if(!buff) - { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); - result = -1; - } - buff = &mii_packet; + qav_state.credit = 0; + result -= do_test(0 + vol, 0x7fffffff + vol2, &qav_state, &port_state); // case6: Push case5 past overflow - last_frame_time = 0; - next_frame_time = 20; - {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); - if(!buff) - { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); - result = -1; - } - buff = &mii_packet; + result -= do_test(0 + vol, 20 + vol2, &qav_state, &port_state); // GROUP2: next_frame_time has wrapped around wrt last_frame_time // case7: now is before next_frame_time - credit = 0; - last_frame_time = 0xfffff800; - next_frame_time = 0x00000800; - {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); - if(!buff) - { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); - result = -1; - } - buff = &mii_packet; - + qav_state.credit = 0; + result -= do_test(0xfffff800 + vol, 0x00000800 + vol2, &qav_state, &port_state); + // case8: wrapping but REALLY big - credit = 0; - last_frame_time = 0xffffffff; - next_frame_time = 0xfffffffe; - {buff, last_copy, credit} = shaper_do_idle_slope(buff, last_frame_time, credit, next_frame_time, qav_idle_slope); - if(!buff) - { - debug_printf("Error: Unexpected buff %s, last_end_time 0x%8x, next_start_time 0x%8x, credit: %d\n", buff != 0 ? "True" : "False", last_frame_time, next_frame_time, credit); - result = -1; - } - buff = &mii_packet; + qav_state.credit = 0; + result -= do_test(0xffffffff + vol, 0xfffffffe + vol2, &qav_state, &port_state); debug_printf("%s\n", result == 0 ? "PASS" : "FAIL"); return result; From d4b45faa8228f87f605130606fc080988438c05d Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 29 Jan 2025 16:09:59 +0000 Subject: [PATCH 18/22] Add passing regex for idle_slope unit --- tests/CMakeLists.txt | 2 +- tests/test_do_idle_slope.expect | 0 tests/test_do_idle_slope_unit.expect | 11 +++++++++++ ...st_do_idle_slope.py => test_do_idle_slope_unit.py} | 6 +++--- .../CMakeLists.txt | 2 +- .../src/main.xc | 0 6 files changed, 16 insertions(+), 5 deletions(-) delete mode 100644 tests/test_do_idle_slope.expect create mode 100644 tests/test_do_idle_slope_unit.expect rename tests/{test_do_idle_slope.py => test_do_idle_slope_unit.py} (79%) rename tests/{test_do_idle_slope => test_do_idle_slope_unit}/CMakeLists.txt (93%) rename tests/{test_do_idle_slope => test_do_idle_slope_unit}/src/main.xc (100%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 30394b09..33bbacf4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,5 +21,5 @@ add_subdirectory(test_check_ifg_wait) add_subdirectory(test_rmii_timing) add_subdirectory(test_rmii_restart) add_subdirectory(test_smi) -add_subdirectory(test_do_idle_slope) +add_subdirectory(test_do_idle_slope_unit) diff --git a/tests/test_do_idle_slope.expect b/tests/test_do_idle_slope.expect deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/test_do_idle_slope_unit.expect b/tests/test_do_idle_slope_unit.expect new file mode 100644 index 00000000..a32e3d16 --- /dev/null +++ b/tests/test_do_idle_slope_unit.expect @@ -0,0 +1,11 @@ +Idle_slope: \d+ +credit_limit: \d+ +Ticks: \d+ +Ticks: \d+ +Ticks: \d+ +Ticks: \d+ +Ticks: \d+ +Ticks: \d+ +Ticks: \d+ +Ticks: \d+ +PASS \ No newline at end of file diff --git a/tests/test_do_idle_slope.py b/tests/test_do_idle_slope_unit.py similarity index 79% rename from tests/test_do_idle_slope.py rename to tests/test_do_idle_slope_unit.py index 739565e9..e47f29eb 100644 --- a/tests/test_do_idle_slope.py +++ b/tests/test_do_idle_slope_unit.py @@ -8,12 +8,12 @@ import Pyxsim as px pkg_dir = Path(__file__).parent -def test_do_idle_slope(capfd): - testname = 'test_do_idle_slope' +def test_do_idle_slope_unit(capfd): + testname = 'test_do_idle_slope_unit' binary = pkg_dir / testname / 'bin' / f'{testname}.xe' assert os.path.isfile(binary) expect_filename = pkg_dir / f'{testname}.expect' - tester = px.testers.ComparisonTester(open(expect_filename)) + tester = px.testers.ComparisonTester(open(expect_filename), regexp=True) result = px.run_on_simulator_( binary, tester=tester, do_xe_prebuild=False, diff --git a/tests/test_do_idle_slope/CMakeLists.txt b/tests/test_do_idle_slope_unit/CMakeLists.txt similarity index 93% rename from tests/test_do_idle_slope/CMakeLists.txt rename to tests/test_do_idle_slope_unit/CMakeLists.txt index 313a86da..530622f2 100644 --- a/tests/test_do_idle_slope/CMakeLists.txt +++ b/tests/test_do_idle_slope_unit/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.21) include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake) -project(test_do_idle_slope) +project(test_do_idle_slope_unit) set(APP_PCA_ENABLE ON) include(../test_deps.cmake) diff --git a/tests/test_do_idle_slope/src/main.xc b/tests/test_do_idle_slope_unit/src/main.xc similarity index 100% rename from tests/test_do_idle_slope/src/main.xc rename to tests/test_do_idle_slope_unit/src/main.xc From 72514c318d02a783b9815fee1011cb20a8bc0cfc Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 29 Jan 2025 16:34:16 +0000 Subject: [PATCH 19/22] Use new shaper in rmii --- lib_ethernet/src/mii_master.xc | 2 +- lib_ethernet/src/rmii_master.xc | 31 ++++++++----------------------- lib_ethernet/src/shaper.h | 1 + 3 files changed, 10 insertions(+), 24 deletions(-) diff --git a/lib_ethernet/src/mii_master.xc b/lib_ethernet/src/mii_master.xc index b4aebf77..96848807 100644 --- a/lib_ethernet/src/mii_master.xc +++ b/lib_ethernet/src/mii_master.xc @@ -487,7 +487,7 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, if (ETHERNET_SUPPORT_HP_QUEUES && enable_shaper) { credit_tmr :> qav_state.current_time; - qav_state.prev_time = qav_state.current_time;; + qav_state.prev_time = qav_state.current_time; } ifg_tmr :> ifg_time; diff --git a/lib_ethernet/src/rmii_master.xc b/lib_ethernet/src/rmii_master.xc index 87ddd8af..919ae75f 100644 --- a/lib_ethernet/src/rmii_master.xc +++ b/lib_ethernet/src/rmii_master.xc @@ -16,6 +16,7 @@ #include "string.h" #include "check_ifg_wait.h" #include "rmii_rx_pins_exit.h" +#include "shaper.h" #define QUOTEAUX(x) #x @@ -992,21 +993,22 @@ unsafe void rmii_master_tx_pins(mii_mempool_t tx_mem_lp, // Flag for readability and faster comparison const unsigned use_4b = (tx_port_width == 4); - int credit = 0; - int credit_time; // Need one timer to be able to read at any time for the shaper timer credit_tmr; // And a second timer to be enforcing the IFG gap hwtimer_t ifg_tmr; unsigned ifg_time = 0; unsigned eof_time = 0; + + qav_state_t qav_state = {0, 0, 1}; // Set times to zero and credit to 1 so it can tx first frame unsigned enable_shaper = p_port_state->qav_shaper_enabled; if (!ETHERNET_SUPPORT_TRAFFIC_SHAPER) { enable_shaper = 0; } if (ETHERNET_SUPPORT_HP_QUEUES && enable_shaper) { - credit_tmr :> credit_time; + credit_tmr :> qav_state.current_time; + qav_state.prev_time = qav_state.current_time;; } ifg_tmr :> ifg_time; @@ -1021,21 +1023,8 @@ unsafe void rmii_master_tx_pins(mii_mempool_t tx_mem_lp, } if (enable_shaper) { - int prev_credit_time = credit_time; - credit_tmr :> credit_time; - - int elapsed = credit_time - prev_credit_time; - credit += elapsed * p_port_state->qav_idle_slope; - - if (buf) { - if (credit < 0) { - buf = 0; - } - } else { - if (credit > 0) { - credit = 0; - } - } + credit_tmr :> qav_state.current_time; + buf = shaper_do_idle_slope(buf, &qav_state, p_port_state); } if (!buf) { @@ -1071,11 +1060,7 @@ unsafe void rmii_master_tx_pins(mii_mempool_t tx_mem_lp, const int packet_is_high_priority = (p_ts_queue == null); if (enable_shaper && packet_is_high_priority) { - const int preamble_bytes = 8; - const int ifg_bytes = 96/8; - const int crc_bytes = 4; - int len = buf->length + preamble_bytes + ifg_bytes + crc_bytes; - credit = credit - (len << (MII_CREDIT_FRACTIONAL_BITS+3)); + shaper_do_send_slope(buf->length, &qav_state); } if (mii_get_and_dec_transmit_count(buf) == 0) { diff --git a/lib_ethernet/src/shaper.h b/lib_ethernet/src/shaper.h index 7088cf62..d92d6ecf 100644 --- a/lib_ethernet/src/shaper.h +++ b/lib_ethernet/src/shaper.h @@ -74,6 +74,7 @@ static inline mii_packet_t * unsafe shaper_do_idle_slope(mii_packet_t * unsafe h qav_state->credit += (int)credit64; } #else + // This is the old code from <4.0.0 qav_state->credit += elapsed_ticks * (int)port_state->qav_idle_slope; // add bit budget since last transmission to credit. ticks * bits/tick = bits #endif From 040303b06aa549576c4fe111ebc11ce9ed92e176 Mon Sep 17 00:00:00 2001 From: Ed Date: Thu, 30 Jan 2025 11:44:30 +0000 Subject: [PATCH 20/22] fix new shaper --- lib_ethernet/src/mii_master.xc | 2 +- lib_ethernet/src/rmii_master.xc | 2 +- lib_ethernet/src/server_state.xc | 3 +- lib_ethernet/src/shaper.h | 10 ++++--- tests/test_do_idle_slope_unit/src/main.xc | 36 +++++++++++++++++++++++ tests/test_shaper/src/main.xc | 2 -- 6 files changed, 46 insertions(+), 9 deletions(-) diff --git a/lib_ethernet/src/mii_master.xc b/lib_ethernet/src/mii_master.xc index 96848807..b6d95d60 100644 --- a/lib_ethernet/src/mii_master.xc +++ b/lib_ethernet/src/mii_master.xc @@ -472,7 +472,7 @@ unsafe void mii_master_tx_pins(mii_mempool_t tx_mem_lp, out buffered port:32 p_mii_txd, volatile ethernet_port_state_t * unsafe p_port_state) { - qav_state_t qav_state = {0, 0, 1}; // Set times to zero and credit to 1 so it can tx first frame + qav_state_t qav_state = {0, 0, 0}; // Set times and credit to zero so it can tx first frame // Need a timer to be able to read at any time for the shaper timer credit_tmr; diff --git a/lib_ethernet/src/rmii_master.xc b/lib_ethernet/src/rmii_master.xc index 919ae75f..b2420938 100644 --- a/lib_ethernet/src/rmii_master.xc +++ b/lib_ethernet/src/rmii_master.xc @@ -1000,7 +1000,7 @@ unsafe void rmii_master_tx_pins(mii_mempool_t tx_mem_lp, unsigned ifg_time = 0; unsigned eof_time = 0; - qav_state_t qav_state = {0, 0, 1}; // Set times to zero and credit to 1 so it can tx first frame + qav_state_t qav_state = {0, 0, 0}; // Set times and credit to zero so it can tx first frame unsigned enable_shaper = p_port_state->qav_shaper_enabled; if (!ETHERNET_SUPPORT_TRAFFIC_SHAPER) { diff --git a/lib_ethernet/src/server_state.xc b/lib_ethernet/src/server_state.xc index a15275f5..a48f916d 100644 --- a/lib_ethernet/src/server_state.xc +++ b/lib_ethernet/src/server_state.xc @@ -8,5 +8,6 @@ void init_server_port_state(ethernet_port_state_t &state, int enable_qav_shaper) memset(&state, 0, sizeof(ethernet_port_state_t)); state.link_state = ETHERNET_LINK_DOWN; state.qav_shaper_enabled = enable_qav_shaper; - state.qav_idle_slope = (11< port_state->qav_credit_limit) { qav_state->credit = port_state->qav_credit_limit; + printstrln("clip"); } else { - qav_state->credit += (int)credit64; + qav_state->credit = (int)credit64; } #else // This is the old code from <4.0.0 @@ -88,7 +89,7 @@ static inline mii_packet_t * unsafe shaper_do_idle_slope(mii_packet_t * unsafe h // Buffer invalid, no HP packet so reset credit as per Annex L of Qav { if (qav_state->credit > 0){ - qav_state->credit = 0; + qav_state->credit = 0; // HP ready to send next time } } @@ -99,6 +100,7 @@ static inline mii_packet_t * unsafe shaper_do_idle_slope(mii_packet_t * unsafe h } } + static inline void shaper_do_send_slope(int len_bytes, qav_state_t * unsafe qav_state){ unsafe{ // Calculate number of additional byte slots on wire over the payload diff --git a/tests/test_do_idle_slope_unit/src/main.xc b/tests/test_do_idle_slope_unit/src/main.xc index 3cd1f11b..91286df7 100644 --- a/tests/test_do_idle_slope_unit/src/main.xc +++ b/tests/test_do_idle_slope_unit/src/main.xc @@ -49,10 +49,46 @@ int do_test(int last_time, int current_time, qav_state_t *qav_state, ethernet_po } } +// For dev only not main test +void do_characterise(void){unsafe{ + ethernet_port_state_t port_state = {0}; + qav_state_t qav_state = {0, 0, 0}; + set_qav_idle_slope(&port_state, 75000000); // Set to max reservation to test for overflows + set_qav_credit_limit(&port_state, ETHERNET_MAX_PACKET_SIZE); + + mii_packet_t mii_packet; + mii_packet_t * unsafe buff = &mii_packet; + + qav_state.prev_time = 0; + qav_state.current_time = 0; + debug_printf("credit: %d\n", qav_state.credit); + + int time_inc = 20 * 8; + + for(int i = 0; i < 20; i++){ + int ishp = ((i % 3) == 0); + if(ishp) buff = &mii_packet; else buff = 0; + qav_state.current_time += 10; + buff = shaper_do_idle_slope(buff, &qav_state, &port_state); + debug_printf("%d %s credit: %d - ", i, ishp == 0 ? "LP":"HP", qav_state.credit); + debug_printf("HP Tx: %s\n", buff != 0 ? "True" : "False"); + + if(buff){ + debug_printf("do send slope\n"); + shaper_do_send_slope(40, &qav_state); + } + qav_state.current_time += time_inc; + + if(i == 10) time_inc = 40 * 8; + } +}} + // To make volatile in port p_vol = XS1_PORT_1A; int main(void){ + do_characterise(); + unsafe{ ethernet_port_state_t port_state; qav_state_t qav_state = {0, 0, 0}; diff --git a/tests/test_shaper/src/main.xc b/tests/test_shaper/src/main.xc index 6a4fbbe7..26b81066 100644 --- a/tests/test_shaper/src/main.xc +++ b/tests/test_shaper/src/main.xc @@ -18,8 +18,6 @@ port p_test_ctrl = on tile[0]: XS1_PORT_1C; #define VLAN_TAGGED 1 -#define MII_CREDIT_FRACTIONAL_BITS 16 - static int calc_idle_slope(int bps) { long long slope = ((long long) bps) << (MII_CREDIT_FRACTIONAL_BITS); From e0d7d00ccbf376913024cb76287619d83f8a30ce Mon Sep 17 00:00:00 2001 From: Ed Date: Thu, 30 Jan 2025 13:15:06 +0000 Subject: [PATCH 21/22] no-limit = 0, Qav test passing --- lib_ethernet/src/server_state.xc | 2 +- lib_ethernet/src/shaper.h | 9 +++++---- lib_ethernet/src/shaper.xc | 9 ++++++++- tests/test_do_idle_slope_unit/src/main.xc | 9 +++++---- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/lib_ethernet/src/server_state.xc b/lib_ethernet/src/server_state.xc index a48f916d..5280d437 100644 --- a/lib_ethernet/src/server_state.xc +++ b/lib_ethernet/src/server_state.xc @@ -8,6 +8,6 @@ void init_server_port_state(ethernet_port_state_t &state, int enable_qav_shaper) memset(&state, 0, sizeof(ethernet_port_state_t)); state.link_state = ETHERNET_LINK_DOWN; state.qav_shaper_enabled = enable_qav_shaper; - state.qav_credit_limit = -1; // No credit limit + state.qav_credit_limit = 0; // No credit limit state.qav_idle_slope = (11<qav_idle_slope + (int64_t)qav_state->credit; - if(credit64 > port_state->qav_credit_limit) + // cast qav_credit_limit as saves a cycle + if((unsigned)port_state->qav_credit_limit && (credit64 > port_state->qav_credit_limit)) { qav_state->credit = port_state->qav_credit_limit; - printstrln("clip"); + // printf("credit: %llu limit: %llu\n", credit64, port_state->qav_credit_limit); } else { qav_state->credit = (int)credit64; } diff --git a/lib_ethernet/src/shaper.xc b/lib_ethernet/src/shaper.xc index 7c53a8c6..588f15ee 100644 --- a/lib_ethernet/src/shaper.xc +++ b/lib_ethernet/src/shaper.xc @@ -18,5 +18,12 @@ void set_qav_idle_slope(ethernet_port_state_t * port_state, unsigned limit_bps) void set_qav_credit_limit(ethernet_port_state_t * port_state, int payload_limit_bytes) { int64_t max_interferring_frame_bits = (preamble_bytes + payload_limit_bytes + crc_bytes + ifg_bytes) * 8; - port_state->qav_credit_limit = max_interferring_frame_bits; + if(payload_limit_bytes > 0){ + port_state->qav_credit_limit = max_interferring_frame_bits; + } + else + { + // No limit + port_state->qav_credit_limit = 0; + } } diff --git a/tests/test_do_idle_slope_unit/src/main.xc b/tests/test_do_idle_slope_unit/src/main.xc index 91286df7..fac1bf49 100644 --- a/tests/test_do_idle_slope_unit/src/main.xc +++ b/tests/test_do_idle_slope_unit/src/main.xc @@ -49,12 +49,13 @@ int do_test(int last_time, int current_time, qav_state_t *qav_state, ethernet_po } } -// For dev only not main test +// For dev only not main test. This is to observe the shaper behaviour void do_characterise(void){unsafe{ ethernet_port_state_t port_state = {0}; qav_state_t qav_state = {0, 0, 0}; set_qav_idle_slope(&port_state, 75000000); // Set to max reservation to test for overflows - set_qav_credit_limit(&port_state, ETHERNET_MAX_PACKET_SIZE); + // set_qav_credit_limit(&port_state, ETHERNET_MAX_PACKET_SIZE); + set_qav_credit_limit(&port_state, 0); mii_packet_t mii_packet; mii_packet_t * unsafe buff = &mii_packet; @@ -73,7 +74,7 @@ void do_characterise(void){unsafe{ debug_printf("%d %s credit: %d - ", i, ishp == 0 ? "LP":"HP", qav_state.credit); debug_printf("HP Tx: %s\n", buff != 0 ? "True" : "False"); - if(buff){ + if(ishp && buff){ debug_printf("do send slope\n"); shaper_do_send_slope(40, &qav_state); } @@ -87,7 +88,7 @@ void do_characterise(void){unsafe{ in port p_vol = XS1_PORT_1A; int main(void){ - do_characterise(); + // do_characterise(); unsafe{ ethernet_port_state_t port_state; From 1590b22bb35abb2b95cefe1ae8eb7f01d992dfdb Mon Sep 17 00:00:00 2001 From: Ed Date: Thu, 30 Jan 2025 13:38:13 +0000 Subject: [PATCH 22/22] Add clipping for no limit case to avoid overflow --- lib_ethernet/src/shaper.h | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib_ethernet/src/shaper.h b/lib_ethernet/src/shaper.h index a2244930..575e4a40 100644 --- a/lib_ethernet/src/shaper.h +++ b/lib_ethernet/src/shaper.h @@ -67,14 +67,24 @@ static inline mii_packet_t * unsafe shaper_do_idle_slope(mii_packet_t * unsafe h #if ETHERNET_SUPPORT_TRAFFIC_SHAPER_CREDIT_LIMIT int64_t credit64 = (int64_t)elapsed_ticks * (int64_t)port_state->qav_idle_slope + (int64_t)qav_state->credit; + // cast qav_credit_limit as saves a cycle - if((unsigned)port_state->qav_credit_limit && (credit64 > port_state->qav_credit_limit)) - { - qav_state->credit = port_state->qav_credit_limit; - // printf("credit: %llu limit: %llu\n", credit64, port_state->qav_credit_limit); + if((unsigned)port_state->qav_credit_limit){ + // Apply limit + if(credit64 > port_state->qav_credit_limit) + { + // printf("limited credit: %lld limit: %lld\n", credit64, port_state->qav_credit_limit); + credit64 = port_state->qav_credit_limit; + } } else { - qav_state->credit = (int)credit64; + // Clip to max_int to avoid overflow + const int64_t max_int = 0x7fffffff; + if(credit64 > max_int){ + // printf("clip credit: %lld limit: %lld\n", credit64, port_state->qav_credit_limit); + credit64 = max_int; + } } + qav_state->credit = (int)credit64; #else // This is the old code from <4.0.0 qav_state->credit += elapsed_ticks * (int)port_state->qav_idle_slope; // add bit budget since last transmission to credit. ticks * bits/tick = bits