NOTICE: The Processors Wiki will End-of-Life on January 15, 2021. It is recommended to download any files or other content you may need that are hosted on processors.wiki.ti.com. The site is now set to read only.

Processor SDK RTOS ICSS-EMAC-Design

From Texas Instruments Wiki
Jump to: navigation, search

Driver Overview[edit]

The ICSS_EMAC (industrial communications subsystem Ethernet Media Access Controller) driver provide APIs to transmit and receive packets with a firmware based ethernet switch that has been implemented on TI’s PRU-ICSS (Programmable Real-Time Unit Subsystem and Industrial Communication SubSystem) 32-bit RISC cores.


Sofware Architecture[edit]

The ICSS EMAC consists of driver + firmware implementation. Firmware refers to the code running on the two PRU's which are part of ICSS while driver refers to that portion of code running on host which is directly associated with the firmware. The two PRU's are responsible for reception of packets while Host runs higher level tasks.

Logically the software on host can be partitioned into

  • Ethernet Driver - Copying packet data and providing to upper layers. Managing PRU/ICSS.
  • TCP/IP and other network stack. NDK in this case
  • RTOS code. SYS/BIOS in this case
  • Peripheral initialization and management

Out of these the first three are relevant to this discussion. Only the driver is discussed in depth while NDK and SYS/BIOS are mentioned in passing whenever relevant.


Driver Features[edit]

The icss_emac LLD implements the following:

    • Rx - Copying the packet received from the firmware and providing it to the TCP/IP stack
    • Tx - Providing packet from TCP/IP stack to the firmware
    • Learning/Forwarding Data Base - refer here
    • Storm Prevention implementation - refer here
    • Host Statistics implementation - refer here
    • TCP/IP stack related initialization
    • Configuring IP address
    • ARM interrupt management
    • Support For Multi ICSS EMAC instance support


EMAC and Switch[edit]

It is important to differentiate between the two different types of implementations in SDK context because this keeps coming up while discussing SDK and it's components. An EMAC example implements two independent Ethernet MAC's using PRU 0/1, they have two different MAC, IP addresses and two different instances of the TCP/IP stack while a switch presents a single IP and MAC address for any external entity.

The icss_emac LLD only provides a simple EMAC loopback example, right now there is no example for a standalone switch on the lines of EMAC.

There are some important differences between an EMAC and Switch

Differences between EMAC and Switch
EMAC Ethernet Switch

(Profinet and EtherNet/IP)

Two interface MAC addresses Single Interface MAC addresses
PRU0 transmits on Port0 and PRU1 on Port1 PRU0 transmits on Port1 and PRU1 on Port0

This configuration is done in software

Two TCP/IP instances and two IP addresses One TCP/IP instance and one IP address
Two Rx interrupts, semaphores and tasks for two ports Single Rx interrupt, semaphore and task
Two ICSS EMAC Handles Single ICSS EMAC Handle
No collision handling (independent MAC's) Colilsion buffer with Collision Handling

Collision refers to Queue contention


Ports in Driver context[edit]

Before beginning it’s important to explain the conventions used in this document, although there are two physical ports for every ICSS for the sake of convenience the Host is considered as a third port. In fact for some protocols this is the logical partitioning used. The convention used here is two physical ports and one host port.

The ports are referred to as

  • Host Port - ICSS_EMAC_PORT_0
  • Physical Port 0 - ICSS_EMAC_PORT_1
  • Physical Port 1 - ICSS_EMAC_PORT_2

This convention is followed throughout the document as well as inside the driver and firmware

Driver Architecture and Memory Map[edit]

A detailed discussion of the architecture is beyond the scope of this document. A brief summary is provided below to explain where the data is copied to and how.

While discussing the ICSS Switch we are mainly concerned with 4 types of memories.

DDR[edit]

This is the memory from where ARM core operates. It's not on the SoC and hence has a lower performance, this is cached.

This contains

  • TCP/IP Buffers.
  • Learning/FDB tables
  • Host Statistics
  • Control structures and variables
L3 OCMC RAM[edit]

This is where the actual Packet buffers or queues are located. There are 15 queues in total. 4 queues for each port (including host) and 1 additional queue for each port to handle collision. This is covered later in QoS section. Developer needs to know that firmware copies the packet data here after receiving them and this is where the driver writes the packet data meant for transmission using the firmware. So this acts as a place holder for packet data before it received or transmitted.

This memory resides inside the SoC (but outside ICSS) and is faster than DDR. It's buffered but non-cached. Size varies from SoC to SoC, please refer to the TRM for more details.

This contains

  • Host receive queues
  • Port transmit queues
  • Collision queues
Shared Data RAM[edit]

This is specific to the PRU subsystem although access is possible from Host albeit slowly. Data common to both PRU's such as Host queue descriptors are stored here. A lot of the memory is available for protocol or application specific usage, for more details refer to the memory map.

Size varies from SoC to SoC, please refer to the TRM for more details.

This contains

  • Host queue descriptors

Note : This memory map is applicable only for EMAC application. Other applications might have their own map.

PRU0 Data RAM[edit]

This is similar to Shared Data RAM though meant for use only by PRU0. Access from PRU1 is also possible, so the separation is only logical, not in hardware.

Size varies from SoC to SoC, please refer to the TRM for more details.

Contains

  • Port 0 Statistics
  • Port 0 MAC ID
  • Port 0 Port, Speed and Duplex information

Note : This memory map is applicable only for EMAC application. Other applications might have their own map.

PRU1 Data RAM[edit]

Similar to PRU0 Data RAM but for PRU1.

Quality of Service and Queues[edit]

Quality of Service is very important for an Ethernet switch as it allows high priority packets to be processed separately from regular packets. This provides reliability for real time traffic.

In EMAC this is done using queues which are mapped to 8 VLAN based priority levels. Each queue is a block of memory on L3 used to store the packet data. Queue sizes may vary and are build time configurable from icss_emacSwitch.h, for example host queue sizes used for default EMAC application are given below, the sizes are denoted by blocks. Each block is 32 bytes in size. The sizes are limited by L3 size which are dictated by SoC.

#define HOST_QUEUE_1_SIZE		194	
#define HOST_QUEUE_2_SIZE		194	
#define HOST_QUEUE_3_SIZE		194	
#define HOST_QUEUE_4_SIZE		194


The transmit queues sizes are denoted separately (allows up to approximately 3KB queue size for each queue).

#define QUEUE_1_SIZE		97 	
#define QUEUE_2_SIZE		97	
#define QUEUE_3_SIZE		97	
#define QUEUE_4_SIZE		97	

So in total there are 15 queues, 4 receive queues for Host and 4 transmit queues for each of the two physical ports. In addition to these there is 1 collision queue each for Host and 2 ports which can hold one packet irrespective of packet size.

The figure below is illustrative to remember this

Switch Queues.png

How QoS works[edit]

When a packet is received in firmware the 3 bit PCP field of a VLAN tag is read and the packet is copied to the appropriate queue based on fixed mapping which maps 2 levels(out of 8) of QoS to one queue. So 7 & 6 map to Queue 0, 5 & 4 to Queue 1 and so on. On the driver this queue number then translates to the priority value and is used to decide how to process the packet. More on this in the next section where driver side Rx processing is discussed in detail.

On Tx side there is no special handling with regards to QoS. Data is copied to one of the four queues based on the queuePriority field of the API ICSS_EmacTxPacket

At the moment driver/firmware doesn't support DSCP


In the testing domain, there might be a need to ping the EVM with the size which greatly exceeds the MTU size, for example doing a ping with size set to 10KB. Because the default configuration above only allows for up to 3KB of buffering per queue on the transmit direction, these parameters will need to be re-tuned to allow successful transmission of the 10 KB ping response. The following is a sample configuration of QUEUE sizes (Note that these settings does not exceed the L3 size which are dictated by the SOC).

#define QUEUE_1_SIZE		13 	
#define QUEUE_2_SIZE		15	
#define QUEUE_3_SIZE		15	
#define QUEUE_4_SIZE		345	/* allows up to approximately 11 KB queue size */


Please refer to [1] for details of how to rebuild the icss-emac LLD PDK component.

Note the NDK's default configuration allows reassembly of packets up to "3012" bytes. To be able to ping bigger sizes, the stack needs to be reconfigured as follows:

  • Change the "MMALLOC_MAXSIZE" definition in "pbm.c" file. (i.e. #define MMALLOC_MAXSIZE

65500) and rebuild the library.

  • Increase the Memory Manager Buffer Page Size in the Buffers tab of the Global configuration.
  • Increase the Maximum IP Reassembly Size property of the IP module configuration

Querying queue fill level[edit]

It's possible to find out the queue fill levels before inserting a new frame or if the intention is to just find out usage levels. The API for this is ICSS_EmacGetMaxQueueFillLevel(). Usage is explained in the Doxygen guide.


Data Path[edit]

Data path refers to the control flow which is executed on the driver and firmware to send or receive a packet. A basic understanding of it goes a long way in explaining the software architecture and if a developer is only trying to use the Rx and Tx capabilities of EMAC or Switch a knowledge of this is sufficient to build an application.

Rx Data Path[edit]

Rx Data path.png

Packets are received in the ICSS from where they are copied by the PRU's to L3 memory. The PRU's then assert an interrupt to tell the Host about the presence of a packet. Till the packet is copied by the Host, PRU does not write over the memory so there is no corruption.

The flowchart shown above shows the sequence in very broad strokes. A detailed description is given below.

  1. PRU Posts an Rx interrupt to the ARM interrupt controller. For EMAC each PRU has a separate Rx interrupt, the configuration for which is done in the application (details in Interrupts)
  2. The interrupt triggers the ISR ICSS_EmacRxInterruptHandler which in turn posts a semaphore rxSemaphoreHandle to signal RxTask to empty the Rx queues.
  3. The RxTask function goes through all the queues, extracts the port/queue number and provides it to an API which copies data from L3 to DDR. The code excerpt is shown below with explanations.
/*Read till all queues are empty*/
while(allQueuesEempty != 1 && numPacketsInLoop <= ((((ICSS_EmacObject*)icssEmacHandle->object)->emacInitcfg)->pacingThreshold))
{
  /*This API reads the queues and gets the queue and port number for each packet*/
  pLength = ICSS_EmacRxPktInfo(icssEmacHandle, &port_number, &queue_number);
  if(pLength > 0) {
   if(queue_number >= ((ICSS_EmacObject*)(icssEmacHandle->object))->emacInitcfg->ethPrioQueue) {
     /*Based on queue priority settings decide if the packet is to be sent to the TCP/IP stack*/
     /*This API is hooked to NDK*/
     icssEmacHwIntRx(&queue_number,icssEmacHandle);
   else {
     /*Protocol specific callback*/
     if(((((ICSS_EmacObject*)icssEmacHandle->object)->callBackHandle)->rxRTCallBack)->callBack != NULL) {
        ((((ICSS_EmacObject*)icssEmacHandle->object)->callBackHandle)->rxRTCallBack)->callBack(&queue_number,
        ((((ICSS_EmacObject*)icssEmacHandle->object)->callBackHandle)->rxRTCallBack)->userArg);
      }
      else {
            /* just dump the packet here so we do no stall the queues*/
            ICSS_EmacRxPktGet(icssEmacHandle,(uint32_t)dest_address, queue_number, &port_number, &more);   
      }
   }

In the code snippet above function ICSS_EmacRxPktInfo goes through each queue one at a time, extracts the port & queue number information for every packet and provides it to functions below. Based on the priority of the packet which is decided by the queue number (refer to discussion on QoS and queues) driver decides to either forward it to NDK, done by icssEmacHwIntRx or give it to the callback function.

The threshold for this decision is decided by the user settable parameter shown in the code above.

((ICSS_EmacObject*)(icssEmacHandle->object))->emacInitcfg->ethPrioQueue)

Anything lower than this configured value goes to the callback function.

If a callback is not registered then the queue is just emptied to prevent queues from overflowing. This is done by the function ICSS_EmacRxPktGet which takes a single packet and copies it into dest_address provided as a parameter. This is not a dummy API but a basic Rx API which performs the basic task of copying data from L3 to DDR, even the NDK API icssEmacHwIntRx calls ICSS_EmacRxPktGet underneath it.

If a developer is building a custom API to process packets then he/she needs to call the API ICSS_EmacRxPktGet in their respective function, this is explained with an example below.

Doing customized packet processing


For example, a user application where some DLR packets are processed different from other packets using a callback.

For this example the value of ethPrioQueue is set to 4 or ICSS_EMAC_QUEUEPRIO4 and in this case these DLR frames have a the highest priority so they go to the callback function, developers need to set an appropriate value for ethPrioQueue based on their requirements. The callback would be configured as follows in teh user application's main routine like this

/*Packet processing callback*/
((((ICSS_EmacObject*)icssEmacHandle->object)->callBackHandle)->rxRTCallBack)->callBack = (ICSS_EmacCallBack)processProtocolFrames;
((((ICSS_EmacObject*)icssEmacHandle->object)->callBackHandle)->rxRTCallBack)->userArg = icssEmacHandle;

Where icssEmacHandle is the main driver handle and processProtocolFrames is the callback function whose outline is given below

void processProtocolFrames(uint32_t* queue_number, void* userArg) {
...
...
uint8_t *dstMacId = tempFrame;
ICSS_EmacHandle eipIcssEmacHandle = (ICSS_EmacHandle)userArg;
/*Fetch the packet*/
size = ICSS_EmacRxPktGet(IcssEmacHandle,(uint32_t)tempFrame, *queue_number, &port, &more); 
...
/*Compare Destination MAC ID and determine if this is a DLR packet*/
if(COMPARE_MAC(dstMacId, dlrMAC)) {
  processDLRFrame(eipIcssEmacHandle, tempFrame, port-1, size);
...
...
}

As can be seen processProtocolFrames function calls ICSS_EmacRxPktGet internally and passes the data to processDLRFrame for further processing.



Tx Data Path[edit]

Tx data path.png

The Transmit path on host is simpler than the Rx path, as far as the developer is concerned the main API call is ICSS_EmacTxPacket, this API implements the Learning/FDB functionality. ICSS_EmacTxPacket in turn calls another API ICSS_EmacTxPacketEnqueue which performs the actual task of copying data from DDR to L3 and signals the PRU to transmit the data.

ICSS_EmacTxPacket when called with the parameter ICSS_EMAC_PORT_0 in portNumber field enables learning/FDB and calls the ICSS_EmacTxPacketEnqueue with the correct port number and when called with parameter ICSS_EMAC_PORT_1 or ICSS_EMAC_PORT_1 the API directly calls the underlying API.

To avoid confusion developers must always call the API ICSS_EmacTxPacket (not ICSS_EmacTxPacketEnqueue) with portNumber as

  • ICSS_EMAC_PORT_0 : If they do not know the port number on which to transmit.
  • ICSS_EMAC_PORT_1 or ICSS_EMAC_PORT_2 : If they know the port number

Usage[edit]

For simple and/or non time critical applications it makes more sense to use the NDK socket API's to perform Receive and Transmit operations. An example using NDK sockets which performs both Rx and Tx is given below. For in depth information on how to use sockets please consult NDK User's guide and NDK API Reference guide.

/*Transmit to System with IP Address*/
#define DST_IP "192.168.1.64"
#define DST_PORT 7
/*Open file session*/
fdOpenSession( (HANDLE)Task_self() );		
SOCKET s = INVALID_SOCKET;
struct sockaddr_in sin1;
struct timeval timeout; 
/*Create UDP socket*/
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
/*Prepare address for connect*/
IPN IPAddr;
IPAddr = inet_addr(DST_IP);
bzero( &sin1, sizeof(struct sockaddr_in) );
sin1.sin_family = AF_INET;
sin1.sin_addr.s_addr = IPAddr;
sin1.sin_port = htons(DST_PORT); 
 /*Configure our Tx and Rx timeout*/
timeout.tv_sec = 0;
timeout.tv_usec = 1;
setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof( timeout ) );
setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof( timeout ) );
/*Transmit packet*/
sendto( s, pBuf, testsize, 0, (PSA)&sin1, sizeof(sin1) );
/*Receive packet*/
recv(s, pBuf, MAX_UDP_SIZE, 0);

For time critical applications with low latency requirements directly calling the API's is recommended. For Transmit this can be done by populating the packet directly(or through a stack) in a memory buffer and calling ICSS_EmacTxPacket with the buffer in a task in main(). Shown below is an example of a periodic transmit being done in main function.

/**A dummy packet*/
uint8_t dummyPkt[ETHERNET_FRAME_SIZE_60] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,
0x45,0x00,0x00,0x2E,0x00,0x00,0x40,0x00,0x40,0x00,0x3A,0xD1};
Void taskSendPacket(UArg a0, UArg a1) {
  /*wait for system to initialize*/
  /*Send packet in a loop every 500ms*/
  Task_sleep(5000);
  while(1) {
    /*Send packet on PORT 1*. Size is known previously/     
    ICSS_EmacTxPacket(emachandle,dummyPkt, ICSS_EMAC_PORT_1, 1, ETHERNET_FRAME_SIZE_60);
    Task_sleep(500);
  }
}

For receive the application/stack can receive the packet data directly in (uint32_t)dest_address from ICSS_EmacRxPktGet inside RxTask. Modify the ethPrioQueue value as per requirement. (Explained above)

Interrupts and Tasks[edit]

This section deals with Interrupts and Tasks required to implement an EMAC LLD application. Since they are tied to the RTOS used, they are exported via the driver handle to the application as well as through the OSAL layer. Developers must take care to maintain correct priorities and order so as not to alter the behavior of the driver.

All the interrupts and tasks enabled in an application can be checked using the SYSBIOS ROV. For more details on this tool refer to the SYS/BIOS section of EMAC LLD Debug Guide.

Interrupts[edit]

There are two types of interrupts

  • ICSS Interrupts : These are interrupts that are routed through the ICSS Interrupt controller to the Host (Refer to section 4.4.2 of AM335x TRM). It consists of interrupts asserted by the PRU as well as interrupts asserted by the peripherals (MDIO, ECAP etc) attached to the ICSS Interrupt controller.

The Host Interrupt controller has 8 usable interrupts mapped to the ICSS interrupt controller, this mapping is programmable and varies from example to example. Every example has a x_pruss_intc_mapping.h file in the sdk/examples folder where x stands for the example name. For EMAC this file is called tiemac_pruss_intc_mapping.h.

Mapping Explained

The interrupt mapping consists of 3 parts

  1. 8 PRU user interrupts (can be set in the firmware by writing to R31) - represented by PRU_ARM_EVENT0 to PRU_ARM_EVENT7. These are part of the 64 system interrupts (out of which 32 are usable). This includes the two link interrupts for two ports MII_LINK0_EVENT and MII_LINK1_EVENT.
  2. 10 ICSS Host channels CHANNEL0 to CHANNEL9 out of which the first two CHANNEL0 and CHANNEL1 are used internally.
  3. 8 ARM PRU interrupts represented by PRU_EVTOUT0 to PRU_EVTOUT7. These can also be seen in the ARM INTC in SoC TRM. Provided below is a screenshot from AM335x TRM showing the 8 interrupts mapped to the PRU.

AM335x-PRUINT.png

The complete mapping follows the pattern

PRU user interrupts --> Host Channels --> ARM PRU Interrupts

To take an example of Rx interrupt for Port 0 (EMAC only). For switch this is the Rx interrupt for both ports.

The following line maps PRU user interrupt 0 to Host channel 2.

{PRU_ARM_EVENT0,CHANNEL2, SYS_EVT_POLARITY_HIGH ,SYS_EVT_TYPE_PULSE}

Host channel CHANNEL2 in turn maps to the first ARM interrupt PRU_ICSS_EVTOUT_0 through this line.

{CHANNEL2, PRU_EVTOUT0}

As seen from the screenshot the interrupt number for PRU_ICSS_EVTOUT_0 on AM335x is 20, so the interrupt number that must be configured for this in application should be 20 if the platform is AM335x. This is done in the following line (defined in main.c)

switchEmacCfg->rxIntNum = 20; 

This mapping alone determines which ARM interrupt number will be associated with a particular PRU user interrupt. For example the line above where PRU user interrupt 0 maps to Host channel 2 can be modified to

{PRU_ARM_EVENT0,CHANNEL2, SYS_EVT_POLARITY_HIGH ,SYS_EVT_TYPE_PULSE} ---> {PRU_ARM_EVENT0,CHANNEL4, SYS_EVT_POLARITY_HIGH ,SYS_EVT_TYPE_PULSE}

and the Channel to ARM interrupt map can be configured as, and the interrupt number on ARM would still remain the same i.e. 20

{CHANNEL2, PRU_EVTOUT0} --->  {CHANNEL4, PRU_EVTOUT0}

A question arises in this case as to the usefulness of CHANNELx. The answer is that channels allow us to map multiple PRU User interrupts and system interrupts to a single channel and in turn to a single ARM interrupt. For example take a look at the link interrupt mapping

{MII_LINK0_EVENT, CHANNEL7, SYS_EVT_POLARITY_HIGH ,SYS_EVT_TYPE_PULSE},	\
{MII_LINK1_EVENT, CHANNEL7, SYS_EVT_POLARITY_HIGH ,SYS_EVT_TYPE_PULSE},	\

and

{CHANNEL7, PRU_EVTOUT6}

This configuration maps both Port 0 and Port 1 interrupts to a single channel and in turn to a single ARM interrupt PRU_ICSS_EVTOUT6, which is interrupt number 26 (shown by the line below in main.c)

switchEmacCfg->linkIntNum=26;

The link interrupt binds to a single ISR ICSS_EmacLinkISR on Host. Inside the ISR an ICSS register HW_ICSS_INTC_SECR1is checked to find out which link event MII_LINK0_EVENT or MII_LINK1_EVENT asserted the interrupt. The advantage of such an approach is that both interrupts are serviced even if they are raised at the same time.

These interrupt numbers can change from SoC to SoC so please consult TRM before making any modifications to the interrupt map. This is also one of the reasons for exporting these configurations to application so that a single driver can handle multiple SoC's others being ease of use, porting other operating systems etc.

PRU interrupts in EMAC
PRU Interrupt number Channel number Host Interrupt number ISR
PRU_ARM_EVENT0 CHANNEL2 20 ICSS_EmacRxInterruptHandler

(emachandle)

PRU_ARM_EVENT1 CHANNEL3 21 ICSS_EmacRxInterruptHandler

(emachandle1)

MII_LINK0_EVENT

Port 0 Link interrupt

CHANNEL7 26 ICSS_EmacLinkISR
MII_LINK1_EVENT

Port 1 Link interrupt

CHANNEL8 27 ICSS_EmacLinkISR
  • Host Interrupts : Host interrupts are interrupts that are not coming via ICSS Interrupt controller. These include peripherals, EDMA, timers etc. A full list is present inside the Interrupt section of any TRM. On AM335x a total of 128 interrupts are available through this (including 8 PRU ICSS interrupts). Discussion of Host interrupts is beyond the scope of this guide as we only deal with the EMAC LLD driver here.

Tasks[edit]

Tasks are the Linux equivalent of processes in SysBIOS. A simple example to create a task from EMAC application is given below

 Task_Params_init(&taskParams);
 taskParams.priority = 15;
 taskParams.instance->name = "SwitchTask";
 Task_create(taskPruss, &taskParams, &eb);

Here taskPruss is given the job of initializing the PRU's and loading the firmware onto them. The task itself is a simple function with two arguments

/*
 *	---task to initialize PRU---
 */
Void taskPruss(UArg a0, UArg a1)
{
  ...
  ...
  ...
}

They can be used for simple tasks like sending or receiving a packet. To get an idea refer to the example usage for sending a packet through a call to transmit API ICSS_EmacTxPacket in a loop.

If calling the task in an endless while loop developers must add a small delay inside the loop using Task_sleep(time in milliseconds) to let other tasks get some time as well, failure to do so is a common mistake that developers make.

Interrupt Pacing[edit]

Interrupt pacing is the technique of pausing Rx interrupt to Host so that Host is not interrupted continuously when receiving high traffic. The scenario is explained in the form of a buffer fill and consumer/producer problem. With pacing enabled interrupt is not given until some threshold has been reached. This allows maximum use of buffer space and also allows Host to perform other tasks.

Interrupt pacing.png

The downside is that if a single frame comes then interrupt is not enabled until certain level is reached, causing a delay in response. So the pacing threshold must be chosen carefully.

Pacing Modes[edit]

Two pacing modes are supported by EMAC/Switch

  1. Pacing mode 1 : This is based on number of frames. When first interrupt is received, interrupts are disabled until a certain threshold of frames are received. The threshold is programmable. The mode is defined by the enum type INTR_PACING_MODE1 and the threshold is defined by the EMAC config variable pacingThreshold
  2. Pacing mode 2 : This is based on time. When first interrupt is received, interrupts are disabled and a timer is enabled. The timer upon expiry triggers an interrupt which enables the interrupt again. The mode is defined by the enum type INTR_PACING_MODE2 and the timer is a compile time option given by DEFAULT_PACING_TIMER_VAL. DMTimer 4 is used by default for this purpose so please make sure that there are no resource usage conflicts.

Half Duplex Support[edit]

Half Duplex support is enabled in the Firmware for EMAC and certain protocols like EtherNet/IP. To enable support, the flag halfDuplexEnable must be enabled in EMAC Config. Another requirement for Half Duplex support is enabling the Collision and Carrier Sense signals in MII. This requires pin muxing, consult the Data Sheet for respective protocols to get more details.

Learning/FDB[edit]

Learning/FDB where FDB stands for Forwarding Data Base is a module that learns source MAC addresses of packets addressed to the Host and thus maintains a list of which devices reside on which port. While transmitting a packet when provided with the destination MAC address the module returns the port number on which the device resides. This avoids duplication of traffic on both ports. This module is applicable only in Switch mode, in EMAC mode this module is disabled since there is only one port.

Design[edit]

Learning table is currently implemented as a Hash table. There is one table for each physical port. Each table has 256 buckets where a bucket has a size of 4. The bucket size and number of buckets are in turn dictated by the choice of Hashing algorithm. A detailed discussion on this topic is beyond the scope of this document, suffice to say that theoretically a hash table is capable of learning 256 * 4 = 1024 entries. The actual capacity may be lower owing to collisions.

A single bucket has

  • Four entries - For storing four MAC Id's
  • Four ageing counters - One associated with each entry
  • Number of Entries - A value which tells how many entries are there in the bucket.

A single table has

  • 256 buckets
  • Total number of entries - Sum of entries in all the buckets
  • Port State - A table has three states
    • Learning - This is the default state. All actions are permitted
    • Not Learning - No new addresses are learnt. Deletions possible.
    • Locked - No additions/deletions allowed

Collisions are handled using ageing counters, one ageing counter is associated with each of the 4 entries inside a bucket. It tells the module which entries are old and which ones are new.

API Guide & Data Structures[edit]

A learning table has the following structure

typedef struct HashTable_t{
       
 uint32_t totalNumEntries;	      /**Total number of entries in the hash table*/
 portState state;		      /**State of the hash table, see enum portState above*/
 HashBucket_t  entries[NUMBUCKETS];  /**Number of bucket entries*/
       
} HashTable_t;

The individual bucket which makes up the learning table has the following structure

typedef struct {
 
 MAC mac[MAX_NUM_ENTRIES];            /**Four MAC Id per bucket*/
 uint8_t timerCount[MAX_NUM_ENTRIES]; /**Timer count used for ageing and conflict resolution*/
 uint8_t numEntries;                  /**Number of MAC entries in the bucket, 4 means it is full*/
  
} HashBucket_t;

The default values are

  • NUMBUCKETS 256
  • MAX_NUM_ENTRIES 4

The algorithm assumes these values and they cannot be changed at present.

API descriptions are only for information, developers are requested to use corresponding IOCTL calls. The IOCTL command for Learning/FDB modules is ICSS_EMAC_IOCTL_LEARNING_CTRL

Adding A MAC address : The corresponding API for this is

void updateHashTable(uint8_t* macId, uint8_t portNum, HashTable_t *tablePtr,ICSS_EmacCallBackConfig* exceptionCallBack)

The API is integrated inside ICSS_EmacRxPktGet so developer need not call it separately, if at all it is required please use the IOCTL call for this. IOCTL Param value is ICSS_EMAC_LEARN_CTRL_UPDATE_TABLE

Looking up an Entry : The corresponding API for this is

uint8_t findMAC(const uint8_t * macId, HashTable_t *tablePtr)

Integrated with driver inside ICSS_EmacTxPacket, use IOCTL Param value ICSS_EMAC_LEARN_CTRL_FIND_MAC

Removing a MAC address : The corresponding API for this is

uint8_t removeMAC(uint8_t * macId, HashTable_t *tablePtr)

Entries are removed automatically upon ageing, if forced removal is required use IOCTL Param value ICSS_EMAC_LEARN_CTRL_REMOVE_MAC

Ageing an Entry : The driver already implements this inside the periodic task which is called every 100 NDK Ticks _HwPktPoll() but users can call it as well. IOCTL Param value ICSS_EMAC_LEARN_CTRL_INC_COUNTER

The corresponding API for this is

void incrementCounter(HashTable_t *tablePtr)

Removing an Aged Entry : The corresponding API for this is

void ageingRoutine(uint8_t portNum, HashTable_t *tablePtr)

Integrated with driver. IOCTL Param value ICSS_EMAC_LEARN_CTRL_AGEING

Changing Port State : Change the port state to appropriate value. This is useful in the implementation of requirements specified by IEEE 802.1D.

The corresponding API for this is

void changePortState(portState state, HashTable_t *tablePtr)

As part of changing port state the module implements locking of a port (where addition/deletion of entries is not possible), ageing (age the entries to simulate passage of time). Not integrated with driver, application must do it. IOCTL Param value ICSS_EMAC_LEARN_CTRL_SET_PORTSTATE

Flushing/Clearing the entire Table : The corresponding API for this is

void purgeTable(uint8_t portNum, HashTable_t *tablePtr)

Not integrated with driver, call separately using IOCTL Param value ICSS_EMAC_LEARN_CTRL_CLR_TABLE

Usage[edit]

The module is integrated with the driver so a developer need not bother about calling the API's separately in the application unless there is a specific need to

  • Add a MAC ID
  • Remove a MAC ID
  • Lock the Port or change it's state

The ageing module is called inside _HwPktPoll (which is a periodic NDK task, more info in the porting guide) via an IOCTL call, to age faster please call the routine separately in another task. Changing the time period of _HwPktPoll is not recommended as many other tasks are performed in this.

Storm Control[edit]

Storm control or Storm prevention is a feature that limits the number of broadcast and multicast packets going to the host and/or cutting through to the other port. Since broadcast and multicast packets are sent over all the ports of a switch they have the potential to create a storm which drowns all other traffic on the network, in this regard this is a very important feature for the switch.

Design[edit]

Storm prevention is implemented on the two PRU's as a credit based scheme. When the feature is enabled, every time a multicast or broadcast packet is received a counter referred to as storm prevention credits is decremented and the packet is sent to the host as well as cut through. If the counter value is 0 then the packet is dropped. The counter is stored on respective PRU DMEM's and is reset after a fixed period by the Host. The combination of this period and credit value decides the rate of acceptance/rejection.

The mechanism is shown below in the diagram

Storm Prevention architecture Industrial.jpeg

The Storm prevention implementation is similar in both PRU's but implemented separately, so it's possible to turn it off selectively for each port. As of now the multicast and broadcast storm prevention functionalities are clubbed together but it is proposed to have them separate in the future.

API Guide & Data Structures[edit]

The main parent structure for Storm Prevention is

typedef struct {
         
 uint8_t suppressionEnabled;   /** enable/disable storm prevention*/
 uint16_t credits;             /** Number of packets allowed in a time interval*/
  
} stormPrevention_t;

There is an instance of this structure for each port

API descriptions are only for information, developers are requested to use corresponding IOCTL calls. The IOCTL command for Learning/FDB modules is ICSS_EMAC_IOCTL_STORM_PREV_CTRL

Enabling Storm Prevention  : The corresponding API for this is

void ICSS_EmacEnableStormPrevention(uint8_t portnum, ICSS_EmacHandle icssEmacHandle)

Corresponding IOCTL Param value is ICSS_EMAC_STORM_PREV_CTRL_ENABLE

Disabling Storm Prevention : Similar to enablement, variable set to False. The corresponding API for this is

void ICSS_EmacDisableStormPrevention(uint8_t portnum, ICSS_EmacHandle icssEmacHandle)

IOCTL Param value is ICSS_EMAC_STORM_PREV_CTRL_DISABLE

Resetting the counters : This is called inside _HwPktPoll which is the NDK tick function. The time period of this tick function (default 100ms) in combination with credits value decides the rate at which Storm Prevention works. This is called by default inside the driver.

The corresponding API for this is

void ICSS_EmacResetStormPreventionCounter(ICSS_EmacHandle icssEmacHandle)

IOCTL Param value is ICSS_EMAC_STORM_PREV_CTRL_RESET

Changing the rate : To change how many packets are accepted or rejected change the value in the structure. At every iteration these values are written to the data RAM by the ICSS_EmacResetStormPreventionCounter API. The scheme through which this occurs is explained in the design description above

The corresponding API for this is

void setCreditValue(uint16_t creditValue, stormPrevention_t* stormPrevPtr)

IOCTL Param value is ICSS_EMAC_STORM_PREV_CTRL_SET_CREDITS

Usage[edit]

Most often Storm Prevention is the main reason for users not being able to receive a packet, esp if the rate is configured incorrectly. So first verify if it is enabled for that port. This can be done by

  • Checking structure variable : See the value of suppressionEnabled variable in the structure.
  • Checking memory : See the memory offset STORM_PREVENTION_OFFSET in the corresponding data RAM. The first bit of the byte location tells you whether the logic is enabled on the port.
  • Disabling Storm Prevention : This is the easiest and preferable for someone not using an emulator. Use the corresponding IOCTL call.

To quickly verify if the logic is indeed dropping packets, try sending some broadcast packets at line rate to the device and check the value of PRU statistics variable stormPrevCounter. See statistics section on how to read this variable.

Statistics[edit]

Statistics on ICSS Switch provide a great deal of information on what's going on with the switch. They are enabled by default and provide provide port specific statistics. They are also a great debugging tool and should be the first thing a developer should look at if they suspect any issue with Rx or Tx.

Design[edit]

The Statistics are divided into

  • Statistics on PRU : Since the LLD functionality is implemented on the PRU's the majority of statistics are implemented on them. The count for each port is stored on the respective Data RAM's starting at the offset STATISTICS_OFFSET. The map is shown below
  • Statistics on Host : The packets coming to the Host are counted once again, this is useful for debugging purposes and to measure throughput (from the PRU to Host) if required. Some statistics like "Unknown protocol type" are only implemented on the Host.

Functionally the statistics are classified into

  • Rx/Tx related statistics : This includes count of broadcast, multicast and unicast packets and their derivatives. Only valid packets are part of this which means that an Rx packet which has been dropped because of storm prevention will not be counted.
  • Error Counters : This includes statistics such as Dropped frames, Rx/Tx errors etc.
  • Other statistics : Includes statistics related to 802.1 CSMA/CD, number of link breaks etc.

A description of PRU statistics along with corresponding memory map is given below.

PRU Statistics Description & Memory Map
Name of Variable Description Name of Offset

Refer to icss_emacSwitch.h

Offset in PRU DRAM 0/1
txBcast Number of broadcast packets sent TX_BC_FRAMES_OFFSET 0x1F00
txMcast Number of multicast packets sent TX_MC_FRAMES_OFFSET 0x1F04
txUcast Number of unicast packets sent TX_UC_FRAMES_OFFSET 0x1F08
txOctets Total number of octets sent, includes all packets TX_BYTE_CNT_OFFSET 0x1F0C
rxBcast Number of broadcast packets received RX_BC_FRAMES_OFFSET 0x1F10
rxMcast Number of multicast packets received RX_MC_FRAMES_OFFSET 0x1F14
rxUcast Number of unicast packets received RX_UC_FRAMES_OFFSET 0x1F18
rxOctets Total number of octets received, includes all packets RX_BYTE_CNT_OFFSET 0x1F1C
tx64byte Transmitted frames with size <= 64 bytes TX_64_BYTE_FRAME_OFFSET 0x1F20
tx65_127byte Transmitted frames with size >= 65 bytes and <= 127 bytes TX_65_127_BYTE_FRAME_OFFSET 0x1F24
tx128_255byte Transmitted frames with size >= 128 bytes and <= 255 bytes TX_128_255_BYTE_FRAME_OFFSET 0x1F28
tx256_511byte Transmitted frames with size >= 256 bytes and <= 511 bytes TX_256_511_BYTE_FRAME_OFFSET 0x1F2C
tx512_1023byte Transmitted frames with size >= 512 bytes and <= 1023 bytes TX_512_1023_BYTE_FRAME_OFFSET 0x1F30
rx64byte Received frames with size <= 64 bytes RX_64_BYTE_FRAME_OFFSET 0x1F34
rx65_127byte Received frames with size >= 65 bytes and <= 127 bytes RX_65_127_BYTE_FRAME_OFFSET 0x1F38
rx128_255byte Received frames with size >= 128 bytes and <= 255 bytes RX_128_255_BYTE_FRAME_OFFSET 0x1F3C
rx256_511byte Received frames with size >= 256 bytes and <= 511 bytes RX_256_511_BYTE_FRAME_OFFSET 0x1F40
rx512_1023byte Received frames with size >= 512 bytes and <= 1023 bytes RX_512_1023_BYTE_FRAME_OFFSET 0x1F44
lateColl Number of packets that suffered collisions late into Tx LATE_COLLISION_OFFSET 0x1F48
singleColl Number of packets that suffered collision only once SINGLE_COLLISION_OFFSET 0x1F4C
multiColl Number of packets that suffered collisions more than once MULTIPLE_COLLISION_OFFSET 0x1F50
excessColl Number of packets that suffered collisions more than 15 times EXCESS_COLLISION_OFFSET 0x1F54
rxMisAlignmentFrames Number of frames with uneven number of bytes in an octet

(This is not tested)

RX_MISALIGNMENT_COUNT_OFFSET 0x1F58
stormPrevCounter Number of packets dropped due to storm prevention STORM_PREVENTION_COUNTER 0x1F5C
macRxError Number of packets with Rx MAC Error RX_ERROR_OFFSET 0x1F60
SFDError Number of packets with incorrect SFD SFD_ERROR_OFFSET 0x1F64
defTx Number of packets deferred at least once due to CS high signal TX_DEFERRED_OFFSET 0x1F68
macTxError Number of packets facing Tx MAC Error TX_ERROR_OFFSET 0x1F6C
rxOverSizedFrames Number of packets >1518 bytes RX_OVERSIZED_FRAME_OFFSET 0x1F70
rxUnderSizedFrames Number of packets < 60 bytes RX_UNDERSIZED_FRAME_OFFSET 0x1F74
rxCRCFrames Frames with CRC/FCS Error RX_CRC_COUNT_OFFSET 0x1F78
droppedPackets Number of Received packets that were not transmitted because of link loss RX_DROPPED_FRAMES_OFFSET 0x1F7C
txOverFlow Transmit FIFO overflow count, not part of standard MIB, for debug TX_FIFO_OVERFLOW_COUNT_OFFSET 0x1F80
txUnderFlow Transmit FIFO underflow count, not part of standard MIB, for debug TX_FIFO_UNDERFLOW_COUNT_OFFSET 0x1F84

API Guide & Data Structures[edit]

As discussed above there are two data structures for Statistics.

  1. PRU based
  2. On Host

Shown below are the members of Host Statistics. The members of PRU statistics are listed in the memory map.

typedef struct {
 
 volatile uint32_t txUcast;			/**Number of unicast packets sent*/
 volatile uint32_t txBcast;			/**Number of broadcast packets sent*/
 volatile uint32_t txMcast;			/**Number of multicast packets sent*/
 volatile uint32_t txOctets;			/**Number of bytes sent*/
 volatile uint32_t rxUcast;			/**Number of unicast packets rcvd*/
 volatile uint32_t rxBcast;			/**Number of broadcast packets rcvd*/
 volatile uint32_t rxMcast;			/**Number of multicast packets rcvd*/
 volatile uint32_t rxOctets;			/**Number of Rx packets*/
 volatile uint32_t rxUnknownProtocol;	        /**Number of packets with unknown protocol*/
 volatile uint32_t linkBreak;			/**Number of link breaks*/
 
}ICSS_EmacHostStatistics_t;

As one can see most of the members are identical to that of PRU statistics (they are a subset) and if all packets are sent to the Host then these member values for PRU and Host statistics should match.

API descriptions are only for information, developers are requested to use corresponding IOCTL calls. The IOCTL command for Statistics module is ICSS_EMAC_IOCTL_STATS_CTRL

Reading PRU Statistics  : To fetch PRU statistics an M2M copy is done from the PRU Data RAM to the PRU statistics structure on DDR. To get the values correctly the memory layout on both sides should be identical. Developers should not modify the member order in pruStatistics_t. Doing so can give incorrect results

Host statistics are updated on the fly in the structure as packets are received (ICSS_EmacUpdateRxStats ) or transmitted (ICSS_EmacUpdateTxStats ) so there is no separate API to collate them.

The corresponding API for this is

void ICSS_EmacReadStats(uint8_t portNum, ICSS_EmacHandle icssEmacHandle)

IOCTL Param value is ICSS_EMAC_IOCTL_STAT_CTRL_GET

Clearing PRU and Host Statistics  : To clear the values do memory write to the structure memory, PRU data RAM and initialize to 0.

The corresponding API for this is

void PurgeStats(uint8_t portNum, ICSS_EmacHandle icssEmacHandle)

IOCTL Param value is ICSS_EMAC_IOCTL_STAT_CTRL_CLEAR

Usage[edit]

Statistics are a great tool to debug issues on the switch. To get them in the application use the IOCTL calls to get and clear statistics

While IOCTL calls provide access to statistics in the application. If someone is using CCS then another quick way to see if there is any activity on the Ports is to directly go to the PRU data RAM offset STATISTICS_OFFSET and see the values directly in memory Refer Debug guide on how to see data RAM values directly in CCS.

Memory Map[edit]

The memory map here refers to the Shared Data RAM memory map in ICSS. L3 map is not of much use to the developer while DDR map is dynamic and is part of the application. The goal of providing this is to help the developer in debugging. Based on the memory map one can directly look at the memory in a CCS + Emulator environment and verify if driver/firmware is working correctly.

What is depicted below is the Memory map for icss_dualmac firmware. The memory map can be found in icss_emacSwitch.h under ti/drv/icss_emac/firmware/icss_dualemac/src. Memory map details for switch firmware can also be found in icss_emacSwitch.h.


ICSS Shared Memory Map
Shared Memory Offset Value PRU0 Data RAM Value PRU1 Data RAM Value
0x0000 - 0x0400 Reserved for Future Use 0x0000 - 0x0400 Reserved for Future Use 0x0000 - 0x0400 Reserved for Future Use
0x0400-0x1CA0 Buffer Descriptor Offsets 0x0400-0x1E98 Available for Protocol/Application 0x0400-0x1E98 Available for Protocol/Application
0x1CA0-0x1FA0 Available for Protocol/Application 0x1E98-0x1F00 Queue Context Offsets, TTS Control Variables 0x1E98-0x1F00 Queue Context Offsets, TTS Control Variables
0x1FA0-0x3000 Empty 0x1F00-0x1FA9 Port0 Statistics

(Map provided above)

0x1F00-0x1FA9 Port1 Statistics

(Map provided above)

0x1F90 Port0 Storm Prevention

Control Variable

0x1F90 Port1 Storm Prevention

Control Variable

0x1F94 Port0 Link Speed

0x64 - 100Mbps

0xA - 10Mbps

0x1F94 Port1 Link Speed

Same as Port0

Any other value is illegal

0x1F98 Port0 Link Status

0x1 - Link Up

0x0 - Link Down

0x1F98 Port1 Link Status

Same as Port0

0x1F98 Port0 Duplex Status

Uses same location as link status, 2nd bit shows duplexity. A 1 in 2nd bit means port is HD

0x1F98 Port1 Duplex Status

Same as Port0

0x1F9E Port0 Control

0x1 - Rx Enabled

0x0 - Rx Disabled

0x1F9E Port1 Control

Same as Port0

0x1FA2 Port0 MAC ID

6 bytes

0x1FA2 Port1 MAC ID

6 bytes

RTOS[edit]

The Processor SDK uses SYS/BIOS as it's RTOS. Configuration for the RTOS is done through RTSC tool chain integrated with CCS. SYS/BIOS is provided free of cost and is a very capable RTOS for typical use cases with minimal latency.

The driver is written in a manner such that there is very little dependency on the Operating System. All dependence on SYS/BIOS is abstracted to OSAL (Operating System Abstraction Layer) and developers are integrate their own operating systems.

OSAL consists of

  1. Interrupt management which is provided as part of TI's OSAL library (ti/osal)
  2. Task and Semaphore management which is provided as part to TI's OSAL library (ti/osal)
  3. Management of peripherals like BIOS Timers which is currently being provided as part of the ICSS_EMAC LLD under the "test" directory. Please refer to ti/drv/icss_emac/test/src/icss_emac_osal.c

Porting Driver to LINUX[edit]

The icss-emac driver was initially written using TI-RTOS. The following areas should be considered when porting to another OS (operating system), for example LINUX.

Memory Map[edit]
  • Memory Map: The ICSS_EMAC driver requires base addresses for different memory regions and control registers to be provided as part of driver initialization. It is the responsibility of the application to configure these addresses with the driver. For TI-RTOS, no address mapping is required and the addresses as defined by the SOC memory map header file provided by the CSL layer can be used directly. For other operating systems, eg LINUX, these addresses need to be mapped to a virtual address space before user space can access them. It will be the responsibility of the user space application to memory map the required regions. This can be accomplished by using the Userspace I/O (UIO) driver. The UIO driver utilized linux like file I/O operations (such as open(), close(), read(), write(), etc) to perform the memory mapping. Once the regions are memory mapped, they can be provided to the driver at time of driver initialization.
Interrupts[edit]
  • Interrupt Registration: For TI-RTOS use case, interrupts generated on the PRU-ICSS core need to be crossbar-ed to an available in interrupt line on the CORE on which the application is running in order for the interrupt to be detected by the CORE. The interrupt and its corresponding ISR then can be registered with SYSBIOS using the OSAL layer provided by the driver and ti-osal library. For LINUX use case, the UIO driver will be required to memory map PRU-ICSS core device interrupts and have them available linux user space application. This is similar to what is required to memory map the regions as specified in the Memory Map section above.


TCP/IP[edit]

The Industrial SDK uses NDK as it's TCP/IP stack. API reference guide here. Like SYS/BIOS the module is imported externally through RTSC (refer Debug Guide) and managed through the application configuration file (am335x_app.cfg and am437x_app.cfg)

NDK recommends it's own abstraction layer which is called NIMU (Network Interface Management Unit). This layer is implemented in the nimu_icss driver (ti/transport/ndk/nimu_icss). Relevant source files are

  • nimu_icssEth.c
  • nimu_icssEthDriver.c
  • nimu_icssSwitchEmac.c
  • nimu_icssMdio.c

Understanding the NIMU layer helps in porting another TCP/IP stack to the example. The NIMU layer is explained in this guide


IOCTL[edit]

IOCTL implementation for the switch drivers is identical to the Unix/Linux based IOCTL calls. They provide the application a convenient method to access driver/kernel space parameters or modify them.

Developers are expected to familiarize themselves with the full list of IOCTL calls so that they can utilize all the features provided. This is even more important when working in an Application/OS kind of environment where access to an emulator is not available.

Design[edit]

The primary IOCTL call is through the API ICSS_EmacIoctl which is implemented in the file icss_emacFwInit.c An IOCTL call uses two parameters to find out which driver API to call

  1. ioctlCommand : Is used to locate the module (Statistics/ Port control etc) which should be called.
  2. ioctlParams : Is used to give module specific instructions

ioctlParams consists of

  • command : Indicates which specific API to execute
  • ioctlVal : Sometimes the API may require specific input, this is used to provide that.

For example to disable receive functionality on a port the following code is used. This code is part of Link interrupt functionality where receive is disabled when PHY detects a link down.

ioctlvalue = ICSS_EMAC_IOCTL_PORT_CTRL_DISABLE;
ioctlParams.ioctlVal = &ioctlvalue;
ICSS_EmacIoctl(icssEmacHandle, ICSS_EMAC_IOCTL_PORT_CTRL, ICSS_EMAC_PORT_1, (void*)&ioctlParams); 

Here ICSS_EMAC_IOCTL_PORT_CTRL refers to the ioctlCommand while ICSS_EMAC_IOCTL_PORT_CTRL_DISABLE is the command part of ioctlParams which tells which action to perform, in this case disabling the port. Port selected is ICSS_EMAC_PORT_1 which refers to Port 1.

A complete list of commands and actions is given below.

API Guide & Data Structures[edit]

IOCTL Command structure

 typedef struct ICSSEMAC_IoctlCmd {
   uint8_t command;
   uint8_t* ioctlVal;
 }ICSSEMAC_IoctlCmd;

There is only one API for IOCTL. It's defined below

uint8_t ICSS_EmacIoctl(ICSS_EmacHandle icssEmacHandle, uint32_t ioctlCommand, uint8_t portNo, void *ioctlParams)

The possible values for ioctlCommand are

  • ICSS_EMAC_IOCTL_PORT_CTRL : Select Port Control operations. Enable/Disable Rx for the specified port
  • ICSS_EMAC_IOCTL_LEARNING_CTRL : Select Learning/FDB module.
  • ICSS_EMAC_IOCTL_STORM_PREV_CTRL : Select Storm Prevention module
  • ICSS_EMAC_IOCTL_STATS_CTRL : Select statistics module

Possible values for ioctlParams are

  • ICSS_EMAC_IOCTL_LEARNING_CTRL : Refer to Learning/FDB API's
    • ICSS_EMAC_LEARN_CTRL_UPDATE_TABLE : Add an entry to the Hash table.
    • ICSS_EMAC_LEARN_CTRL_CLR_TABLE : Clear the Learning Table for the specified port.
    • ICSS_EMAC_LEARN_CTRL_AGEING : Age out old entries from the table.
    • ICSS_EMAC_LEARN_CTRL_FIND_MAC : Find the port number given a MAC ID.
    • ICSS_EMAC_LEARN_CTRL_REMOVE_MAC : Remove a MAC ID from the Learning Table
    • ICSS_EMAC_LEARN_CTRL_INC_COUNTER : Age the entries with time by calling this periodically
    • ICSS_EMAC_LEARN_CTRL_INIT_TABLE : Initialize the learning table
    • ICSS_EMAC_LEARN_CTRL_SET_PORTSTATE : Set the Port state to value defined by the enum type portState
  • ICSS_EMAC_IOCTL_STATS_CTRL : Refer to Statistics API's
    • ICSS_EMAC_IOCTL_STAT_CTRL_GET : Get the statistics
    • ICSS_EMAC_IOCTL_STAT_CTRL_CLEAR : Clear all stat counters
  • ICSS_EMAC_IOCTL_STORM_PREV_CTRL : Refer to Storm prevention API's
    • ICSS_EMAC_STORM_PREV_CTRL_ENABLE : Enable Storm Prevention
    • ICSS_EMAC_STORM_PREV_CTRL_DISABLE : Disable Storm Prevention
    • ICSS_EMAC_STORM_PREV_CTRL_SET_CREDITS : Set how many BC/MC packets are allowed in a period of 100 NDK Ticks (Time period of _HwPktPoll())
    • ICSS_EMAC_STORM_PREV_CTRL_INIT : Initialize Storm Prevention
    • ICSS_EMAC_STORM_PREV_CTRL_RESET : Renew the storm prevention counters, this allows more packets to come through once existing credits have expired. This must be called periodically in _HwPktPoll()
  • ICSS_EMAC_IOCTL_PORT_CTRL : For Rx Enable/Disable operations. There are no sub-commands here. Whether to enable or disable is decided by the value of ioctlVal

Usage[edit]

Using IOCTL to debug the issues is encouraged. This is easier than trying to connect an emulator and reading the values at run time. There are numerous instances of it's usage. A quick search for the API ICSS_EmacIoctl throughout the code reveals numerous examples. One of them pertaining to Storm Control module is shown below.

In this example the storm prevention counters are reset in PRU Data RAM using IOCTL. The if/else refers to Switch or EMAC mode, rest of the code is self-explanatory

 ioctlParams.command = ICSS_EMAC_STORM_PREV_CTRL_RESET;
 /*Reset the credit values used for Storm prevention*/
 if(ICSS_EMAC_MODE_SWITCH == ((ICSS_EmacObject*)(pi->nimuDrvHandle)->object)->emacInitcfg->portMask)
 {
   strmPreventionEnable1 = (stormPrevention_t*)(((ICSS_EmacObject*)(pi->nimuDrvHandle)->object)->stormPrevPtr);
   strmPreventionEnable2 = (stormPrevention_t*)(((ICSS_EmacObject*)(pi->nimuDrvHandle)->object)->stormPrevPtr +1);
   if(strmPreventionEnable1->suppressionEnabled ||	strmPreventionEnable2->suppressionEnabled)
   	ICSS_EmacIoctl(pi->nimuDrvHandle, ICSS_EMAC_IOCTL_STORM_PREV_CTRL, NULL, (void*)&ioctlParams);
 }
 else
 {
   strmPreventionEnable1 = (stormPrevention_t*)(((ICSS_EmacObject*)(pi->nimuDrvHandle)->object)->stormPrevPtr);
   if(strmPreventionEnable1->suppressionEnabled)
   	ICSS_EmacIoctl(pi->nimuDrvHandle, ICSS_EMAC_IOCTL_STORM_PREV_CTRL, NULL, (void*)&ioctlParams);
 }

ICSS EMAC LLD Dependencies[edit]

ICSS EMAC LLD is dependent on the Application/Transport layer for its proper functioning. These dependencies have been consolidated and exported to the application layer (example) to make it easy for developer to integrate the LLD with their own RTOS and TCP/IP stack.

Interrupt Configuration[edit]

ICSS EMAC LLD expects the Interrupt configuration to come from Application/Transport layer. The interrupt configuration is explained in detail in the interrupts section. Please refer to it for more details.

The LLD makes use of two interrupts for copying packets from the queues to the TCP/IP stack.

Rx Interrupt[edit]

The ICSS EMAC LLD depends on the Rx interrupt for receiving packets.The application should do the interrupt creation and should use the API ICSS_EmacRxInterruptHandler as the ISR.This will enable the ICSS EMAC LLD to receive packets. The user should make sure the arm interrupt (Rx) is mapped to correct PRUSS Event. The PRUSS Event that is used for Rx will be determined by the firmware which will be used along with the ICSS EMAC LLD. The LLD expects a single interrupt for both Ports in Switch Mode and single interrupt per Port in Emac Mode.

Link Interrupt[edit]

Link Interrupt informs the ICSS EMAC LLD of any Link state change. This is where the LLD informs the firmware about the Link status and the Phy configuration.The LLD expects single interrupt for both Ports in Switch Mode and a single interrupt per Port in Emac Mode. In case the Interrupt mechanism is not available, the application should call ICSS_EmacLinkISR whenever there is a link change.


Transmit Complete Interrupt[edit]

Transmit Complete Interrupt informs the ICSS EMAC LLD when the PRU-ICSS firmware has finished placing the frame be be transmitted on the wire. The LLD expects a single interrupt for both Ports in Switch Mode and single interrupt per Port in Emac Mode.

Learning module Increment counter implementation[edit]

This is required for Switch implementation only. The application needs to call the IOCTL periodically, this is already done by the NDK. Refer to the API section of Learning for more details.

MDIO Configurations[edit]

ICSS EMAC LLD does not do any of the MDIO configurations. It expects the application to do following MDIO operations

  • MDIO Initialization
  • MDIO Link Interrupt enable if MDIO Link interrupt is used

Please refer to ti/drv/icss_emac/test/src/test_mdio.c for implementation details.


EMAC Configuration and How To[edit]

Configuration Parameters[edit]

EMAC configuration is defined by the structure ICSSEMAC_InitConfig The structure members are

  • phyAddr : is a two element array which contains the MDIO PHY Address for each of the two ports.
  • halfDuplexEnable : If Half Duplex must be supported then this variable has to be enabled. Please note that Half Duplex also must be supported by the specific protocol firmware
  • enableIntrPacing : Enable interrupt pacing. Interrupt pacing is explained above.
  • pacingThreshold : Threshold for number of frames in interrupt pacing mode 1. See explanation of interrupt pacing.
  • ethPrioQueue : If there is a packet in this queue or a queue number higher than this then the frame goes to TCP/IP stack, else it goes to the registered handler. See this for explanation. Valid values are ICSS_EMAC_QUEUE1 to ICSS_EMAC_QUEUE4
  • learningEn : As name suggests this enables/disables FDB learning on the switch. This flag is not applicable to EMAC.
  • portMask : Specify if device is acting as a switch or MAC. Valid values are
    • ICSS_EMAC_MODE_SWITCH : Switch Mode
    • ICSS_EMAC_MODE_MAC1 : Enable Port 1 MAC
    • ICSS_EMAC_MODE_MAC2 : Enable Port 2 MAC
    • ICSS_EMAC_MODE_DUALMAC : Enable both ports in MAC mode.
  • rxIntNum : Rx interrupt number for Host. See Interrupt mapping section for explanation.
  • linkIntNum : Link interrupt number for Host. See above.
  • macId : Pointer to 6 byte MAC ID.
  • intrPacingMode : Which pacing mode to use. See interrupt pacing explanation.

Network related[edit]

How to configure IP address and other IPv4 parameters[edit]

IP address, network mask and other params can be set through the NDK configuration file. An example of such configuration file can be found in ti/transport/ndk/nimu_icss/example/am572x/armv7/biosnimu_icss_arm_wSocLib.cfg. You will see "Ip" configuration parameters that can be set for ip address, network mask and gateway ip address.


How to configure MAC address[edit]

MAC address is variable macId in the configuration ICSS_EmacInitConfig. It can be set as per the application requirements. The following excerpt from ethernet_mac example shows how to do this

/*Get MAC address from eFUSE*/
SOCCtrlGetPortMacAddr(1,lclMac);
/*Assign MAC ID for that particular port*/
switchEmacCfg->macId = lclMac;


Additional documentation reference[edit]

Document Location
API Reference Manual $(TI_PDK_INSTALL_DIR)\packages\ti\drv\icss_emac\docs\doxygen\html\index.html
Release Notes $(TI_PDK_INSTALL_DIR)\packages\ti\drv\icss_emac\docs\ReleaseNotes_ICSS_EMAC_LLD.pdf
E2e.jpg {{
  1. switchcategory:MultiCore=
  • For technical support on MultiCore devices, please post your questions in the C6000 MultiCore Forum
  • For questions related to the BIOS MultiCore SDK (MCSDK), please use the BIOS Forum

Please post only comments related to the article Processor SDK RTOS ICSS-EMAC-Design here.

Keystone=
  • For technical support on MultiCore devices, please post your questions in the C6000 MultiCore Forum
  • For questions related to the BIOS MultiCore SDK (MCSDK), please use the BIOS Forum

Please post only comments related to the article Processor SDK RTOS ICSS-EMAC-Design here.

C2000=For technical support on the C2000 please post your questions on The C2000 Forum. Please post only comments about the article Processor SDK RTOS ICSS-EMAC-Design here. DaVinci=For technical support on DaVincoplease post your questions on The DaVinci Forum. Please post only comments about the article Processor SDK RTOS ICSS-EMAC-Design here. MSP430=For technical support on MSP430 please post your questions on The MSP430 Forum. Please post only comments about the article Processor SDK RTOS ICSS-EMAC-Design here. OMAP35x=For technical support on OMAP please post your questions on The OMAP Forum. Please post only comments about the article Processor SDK RTOS ICSS-EMAC-Design here. OMAPL1=For technical support on OMAP please post your questions on The OMAP Forum. Please post only comments about the article Processor SDK RTOS ICSS-EMAC-Design here. MAVRK=For technical support on MAVRK please post your questions on The MAVRK Toolbox Forum. Please post only comments about the article Processor SDK RTOS ICSS-EMAC-Design here. For technical support please post your questions at http://e2e.ti.com. Please post only comments about the article Processor SDK RTOS ICSS-EMAC-Design here.

}}

Hyperlink blue.png Links

Amplifiers & Linear
Audio
Broadband RF/IF & Digital Radio
Clocks & Timers
Data Converters

DLP & MEMS
High-Reliability
Interface
Logic
Power Management

Processors

Switches & Multiplexers
Temperature Sensors & Control ICs
Wireless Connectivity