Author Topic: Kinetis K70 / K61 Ethernet optimisation  (Read 7438 times)

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Kinetis K70 / K61 Ethernet optimisation
« on: August 08, 2012, 06:25:41 PM »
Hi All

The Kinetis has an Ethernet EMAC peripheral (when available) based on the one used in the Coldfire but with some extensions. The EMAC is based on buffer descriptors which are created and controlled in SRAM and these buffer descriptors work basically in big-endian mode. This suited the Coldfire but required some big / little-endian swapping in code when used with the Cortex M4 based Kinetis.

It happens that the K70 and K61 derivatives now allow the endian mode to be configured so that the buffer descriptors can operate in the more natural (for the ARM) mode which saves having to do this swapping in code and there results in a small improvement in the code efficiency. Although maybe not a huge saving every bit helps and so the following change has been made to make use of this capability.

1) When the chip supports this it will have an extra bit called ETHER_DBSWP in the EMAC ECR register. Therefore this bit is made conditional on the processor used:

#define ECR                  *(volatile unsigned long *)(EMAC_BASE_ADD + 0x24) // Ethernet Control Register
  #define RESET_FEC          0x00000001                                  // issue MAC reset (self-clearing)
  #define ETHER_EN           0x00000002                                  // enable ethernet operation
  #define ETHER_MAGICEN      0x00000004                                  // enable magic package detection
  #define ETHER_SLEEP        0x00000008                                  // enable sleep mode
  #define ETHER_EN1588       0x00000010                                  // enable enhanced functionality
  #define ETHER_DBGEN        0x00000040                                  // enter freeze mode when debuggig
  #define ETHER_STOPEN       0x00000080                                  // enabled in doze mode
#if defined KINETIS_K61 || defined KINETIS_K70
  #define ETHER_DBSWP        0x00000100                                  // enabled descriptor byte swapping to support little-endian devices
#endif


2) Then the definitions of the buffer descriptors and their content are conditional on the presence of the bit:

#if defined ETHER_DBSWP                                                  // natural little-endian
    typedef struct stKINETIS_FEC_BD
    {
        volatile unsigned short usBDLength;
        volatile unsigned short usBDControl;
        unsigned char *ptrBD_Data;
    #ifdef EMAC_ENHANCED                                                 // additional fields available in enhanced mode
        volatile unsigned long  ulBDControlEnhanced;
        volatile unsigned short usPayloadCS;                             // only receiver
        volatile unsigned short usRxInfoHeaderProt;                      // only receiver
        volatile unsigned long  ulBDU;
        volatile unsigned long  ul1588_timestamp;
        unsigned long  ulRes[2];
    #endif
    } KINETIS_FEC_BD;

    #define EMPTY_BUFFER         0x8000                                  // RX BD Control bits
    #define RECEIVE_OWNERSHIP_1  0x4000                                  // can be optionally used by software
    #define WRAP_BIT_RX          0x2000
    #define RECEIVE_OWNERSHIP_2  0x1000                                  // can be optionally used by software
    #define LAST_IN_FRAME_RX     0x0800
    #define RECEIVE_MISS         0x0100                                  // received due to promiscuouse mode only
    #define RECEIVE_BROADCAST    0x0080                                  // received due to broadcast address
    #define RECEIVE_MULTICAST    0x0040                                  // received due to multicast address
    #define RECEIVE_LENGTH_VIOL  0x0020                                  // receive frame length violation
    #define RECEIVE_NON_OCTET_AL 0x0010                                  // non-octet aligned frame
    #define RECEIVE_CRC_ERROR    0x0004                                  // receive CRC or frame error
    #define OVERRUN_FRAME        0x0002
    #define TRUNCATED_FRAME      0x0001

    // Enhanced
    //
    #define RX_MAC_ERROR         0x80000000
    #define RX_PHY_ERROR         0x04000000
    #define RX_COLLISION         0x02000000
    #define RX_UNICAST           0x01000000
    #define RX_GEN_INTERRUPT     0x00800000
    #define RX_IP_CS_ERROR       0x00000020
    #define RX_PROT_CS_ERROR     0x00000010
    #define RX_VLAN              0x00000004
    #define RX_IPV6              0x00000002
    #define RX_IPV4_FRAG         0x00000001

    #define RX_HEADER_LEN_MASK   0xf800
    #define RX_PROT_TYPE_MASK    0x00ff


    #define READY_TX             0x8000                                  // TX BD Control bits
    #define TRANSMIT_OWNERSHIP_1 0x4000                                  // can be optionally used by software
    #define WRAP_BIT_TX          0x2000
    #define TRANSMIT_OWNERSHIP_2 0x1000                                  // can be optionally used by software
    #define LAST_IN_FRAME_TX     0x0800
    #define TX_CRC               0x0400
    #define TX_ABC               0x0200                                  // append bad CRC - not supported in enhanced mode

    // Enhanced
    //
    #define TX_GENERATE_INT      0x40000000
    #define TX_ADD_TIMESTAMP     0x20000000
    #define TX_INSERT_PROT_CS    0x10000000
    #define TX_INSERT_IP_CS      0x08000000
    #define TX_ERROR_OCCURRED    0x00008000
    #define TX_UNDERFLOW_ERROR   0x00002000
    #define TX_EXCESS_COLLISIONS 0x00001000
    #define TX_FRAME_ERROR       0x00000800
    #define TX_LATE_COLLISION    0x00000400
    #define TX_OVERFLOW_ERROR    0x00000200
    #define TX_TIMESTAMP_ERROR   0x00000100

    #define BD_UPDATE_DONE       0x80000000                              // rx and tx

#else                                                                    // big-endian representation
    typedef struct stKINETIS_FEC_BD
    {
        volatile unsigned short usBDControl;
        volatile unsigned short usBDLength;
        unsigned char *ptrBD_Data;
    #ifdef EMAC_ENHANCED                                                 // additional fields available in enhanced mode
        volatile unsigned long  ulBDControlEnhanced;
        volatile unsigned short usRxInfoHeaderProt;                      // only receiver
        volatile unsigned short usPayloadCS;                             // only receiver
        volatile unsigned long  ulBDU;
        volatile unsigned long  ul1588_timestamp;
        unsigned long  ulRes[2];
    #endif
    } KINETIS_FEC_BD;

    #define EMPTY_BUFFER         0x0080                                  // RX BD Control bits
    #define RECEIVE_OWNERSHIP_1  0x0040                                  // can be optionally used by software
    #define WRAP_BIT_RX          0x0020
    #define RECEIVE_OWNERSHIP_2  0x0010                                  // can be optionally used by software
    #define LAST_IN_FRAME_RX     0x0008
    #define RECEIVE_MISS         0x0001                                  // received due to promiscuouse mode only
    #define RECEIVE_BROADCAST    0x8000                                  // received due to broadcast address
    #define RECEIVE_MULTICAST    0x4000                                  // received due to multicast address
    #define RECEIVE_LENGTH_VIOL  0x2000                                  // receive frame length violation
    #define RECEIVE_NON_OCTET_AL 0x1000                                  // non-octet aligned frame
    #define RECEIVE_CRC_ERROR    0x0400                                  // receive CRC or frame error
    #define OVERRUN_FRAME        0x0200
    #define TRUNCATED_FRAME      0x0100

    // Enhanced
    //
    #define RX_MAC_ERROR         0x00000080
    #define RX_PHY_ERROR         0x00000004
    #define RX_COLLISION         0x00000002
    #define RX_UNICAST           0x00000001
    #define RX_GEN_INTERRUPT     0x00008000
    #define RX_IP_CS_ERROR       0x20000000
    #define RX_PROT_CS_ERROR     0x10000000
    #define RX_VLAN              0x04000000
    #define RX_IPV6              0x02000000
    #define RX_IPV4_FRAG         0x01000000

    #define RX_HEADER_LEN_MASK   0x00f8
    #define RX_PROT_TYPE_MASK    0xff00


    #define READY_TX             0x0080                                  // TX BD Control bits
    #define TRANSMIT_OWNERSHIP_1 0x0040                                  // can be optionally used by software
    #define WRAP_BIT_TX          0x0020
    #define TRANSMIT_OWNERSHIP_2 0x0010                                  // can be optionally used by software
    #define LAST_IN_FRAME_TX     0x0008
    #define TX_CRC               0x0004
    #define TX_ABC               0x0002                                  // append bad CRC - not supported in enhanced mode

    // Enhanced
    //
    #define TX_GENERATE_INT      0x00000040
    #define TX_ADD_TIMESTAMP     0x00000020
    #define TX_INSERT_PROT_CS    0x00000010
    #define TX_INSERT_IP_CS      0x00000008
    #define TX_ERROR_OCCURRED    0x00800000
    #define TX_UNDERFLOW_ERROR   0x00200000
    #define TX_EXCESS_COLLISIONS 0x00100000
    #define TX_FRAME_ERROR       0x00080000
    #define TX_LATE_COLLISION    0x00040000
    #define TX_OVERFLOW_ERROR    0x00020000
    #define TX_TIMESTAMP_ERROR   0x00010000

    #define BD_UPDATE_DONE       0x00000080                              // rx and tx
#endif


Note that the buffer descriptor content is swapped between little-and big-endian and also some of the elements in the KINETIS_FEC_BD struct have a different ordering to suit.

3) When accesses to some buffer descriptor values (like data length and address pointers) are made macros are used which are defined as below. When the buffer decriptors can work in little-endian mode the macros are simply made to directly use the value without any extra work:

#if defined _WINDOWS || defined ETHER_DBSWP                              // device with ETHER_DBSWP control in the ECR register can configure the EMAC buffer decriptors to use little-endian mode
    #define fnLE_ENET_add(x)  x
    #define fnLE_ENET_word(x) x
#else
    // Convert between little and big-endian address
    //
    #define fnLE_ENET_add(x) (unsigned char *)(((unsigned long)(x) >> 24) | (((unsigned long)(x) >> 8) & 0x0000ff00) | (((unsigned long)(x) << 8) & 0x00ff0000) | (((unsigned long)(x) << 24) & 0xff000000))

    // Convert between little and big-endian short word
    //
    #define fnLE_ENET_word(x) (((unsigned short)(x) >> 8) | ((unsigned short)(x) << 8))
#endif


4) Finally, when the Ethernet operation is enabled - or re-enabled, the little-endian mode is selected by

ECR = ENABLE_ETHERNET_OPERATION;

where the value of ENABLE_ETHERNET_OPERATION is made conditional on whether enhanced mode is used or whether the chip supports the little-endian mode by using the following:

#ifdef EMAC_ENHANCED
    #if defined ETHER_DBSWP                                              // if the EMAC supports buffer descriptors in little-endian format enable this mode
        #define ENABLE_ETHERNET_OPERATION (ETHER_EN | ETHER_EN1588 | ETHER_DBSWP)
    #else
        #define ENABLE_ETHERNET_OPERATION (ETHER_EN | ETHER_EN1588)
    #endif
#else
    #if defined ETHER_DBSWP                                              // if the EMAC supports buffer descriptors in little-endian format enable this mode
        #define ENABLE_ETHERNET_OPERATION (ETHER_EN | ETHER_DBSWP)
    #else
        #define ENABLE_ETHERNET_OPERATION (ETHER_EN)
    #endif
#endif


Therefore by changing a few defines K70 and K61 processors automatically take advantage of this capability...

Good luck!

Regards

Mark

P.S. The above has been tested and will be automatically included in the next Kinetis project release.

« Last Edit: August 08, 2012, 06:34:00 PM by mark »