Monday, December 24, 2018

Porting lwIP for embOS


This is my second article. In this, I`m going to write about the experience of porting lwIP for embOS platfrom and SAMA5D3x architecture. As you know porting anything to different platform is always challenging as any simple mistake could cost you quite a lot of debugging time, porting TCP/IP stack is much more challenging.
 Please note that this is not how to do article, in fact I`m not even going to give the definition of all the necessary functions need to be written for lwIP stack, as it could make this blog a mini book.
 To port lwIP for embOS or any other platform, we need to provide the definitions for the functions as required by the lwIP stack ( function list can be found in the reference link ). Below are the definitions for some of those functions in the list. 


sys_sem_new:


               In this function, we need to create semaphore for the stack and need to return the ID of the newly created semaphore ( or address depends on your definition of sys_sem_t ). As embOS needs the address of the OS_RSEMA variable each time ( which should be unique), we need to find whether the given OS_RSEMA variable is used or not, simple way to do that is having one structure containing OS_RSEMA variable and the status flag indicating whether the corresponding semaphore is used or not. We will loop through the array and find the unused semaphore and return its address. We also need to change the flag status as used. If the count is zero, we need to take the semaphore, that is done by OS_Use function call, so that task requiring semaphore would be blocked till the semaphore is released by the stack. ( In all the coming function definitions, returning error code can be handled better with single return statement. For time being I`ve written multiple return statements within single function )

typedef OS_RSEMA* sys_sem_t;


typedef struct _semWrapper

{

   OS_RSEMA tcpSemaphore;

   uint8_t usedFlag;  

}semWrapper;


static semWrapper tcpSemArray[MAX_SEM_STRUCT];


sys_sem_t sys_sem_new(u8_t count)

{

   uint8_t locLoopCount = 0;

  

   for(locLoopCount = 0;locLoopCount < MAX_SEM_STRUCT;locLoopCount++)

   {

      if(!(tcpSemArray[locLoopCount].usedFlag))

      {

         break;

      }

   }

   if(locLoopCount >= MAX_SEM_STRUCT)

   {

      return NULL;                                               /* TBD - Debug Print to be added - msarul */

   }

   sys_sem_t  xSemaphore = &(tcpSemArray[locLoopCount].tcpSemaphore);

   tcpSemArray[locLoopCount].usedFlag = 1;

  OS_CreateRSema(xSemaphore);

   if(count == 0)                                                /* Means it can't be taken */

   {

      OS_Use(xSemaphore);

   }

   return xSemaphore;

}


sys_sem_free:


               Here we will loop through the array to find the match of the given semaphore and we will delete the same using OS_DeleteRSema function and we will change the flag status into unused.

void sys_sem_free(sys_sem_t sem)

{

   uint8_t locLoopCount = 0;

  

   for(locLoopCount = 0;locLoopCount < MAX_SEM_STRUCT;locLoopCount++)

   {

      if(sem == (&(tcpSemArray[locLoopCount].tcpSemaphore)))

      {

         break;

      }

   }

   if(MAX_SEM_STRUCT <= locLoopCount)

   {

      return;                /* TBD - Debug Print to be added. Shouldn`t come here, msarul */

   }

   tcpSemArray[locLoopCount].usedFlag = 0;

   OS_DeleteRSema( sem );

}


sys_arch_sem_wait:


               We need to wait for the semaphore to be released, in case if timeout is provided and if it is elapsed we need to return the mentioned error ( SYS_ARCH_TIMEOUT ). Otherwise we need to return the elapsed time.

u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)

{

   int startTime, endTime, elapsed;

  

   startTime = OS_GetTime();

  

   if( timeout != 0 )

   {

      if( OS_UseTimed( sem, timeout ) != NULL )

      {

         endTime = OS_GetTime();
         if ( endTime >= startTime )
         {
            elapsed = ( endTime - startTime );
         }
         else
         {

            elapsed = ( endTime + ( 0xFFFFFFFF - startTime ) + 1 ) ;
         }

         if( elapsed == 0 )

         {

            elapsed = 1;

         }

         return (elapsed);

      }

      else

      {

         return SYS_ARCH_TIMEOUT;

      }

   }

   else     /* must block without a timeout */

   {

      OS_Use(sem);

      endTime = OS_GetTime();

      if ( endTime >= startTime )
      {
         elapsed = ( endTime - startTime );
      }
      else
      {

         elapsed = ( endTime + ( 0xFFFFFFFF - startTime ) + 1 ) ;
      }

      if( elapsed == 0 )

      {

         elapsed = 1;

      }

      return ( elapsed );

   }

}


sys_sem_signal:


               OS_Unuse function releases the provided semaphore, so that task blocked on the same can continue its execution.

void sys_sem_signal(sys_sem_t sem)

{

   OS_Unuse( sem );

}


sys_mbox_trypost:


               In this function we will put the address stored in the given message pointer in the message queue. One important thing to note here is we are not passing the given data pointed by specified pointer through message queue, we`re passing the content of the pointer ( which is address of the message ) through message queue, so during fetching we need to take care of this, will see that in explanation of fetch function. If you look at embOS manual about OS_Q_Put function, it says argument pSrc should point to the message to store. So the statement “ OS_Q_Put(mbox,&msg,4)) ” causes the address stored in the given pointer message ( msg ), passed as data in the message queue.

err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg)

{

   if(NULL == OS_Q_Put(mbox,&msg,4))

   {

      return ERR_OK;

   }

   else

   {

      return ERR_MEM;

   }

}



sys_arch_mbox_tryfetch:


u32_t sys_arch_mbox_tryfetch( sys_mbox_t mbox, void **msg )
{

   void *dummyptr = NULL;

   unsigned int * desPointer = NULL;

  

   if( msg == NULL )

   {

      msg = &dummyptr;

   }

  

   if(NULL != OS_Q_GetPtrCond( mbox, (void *)(&desPointer)) )

   {

      *msg = (void *)(*desPointer);

      OS_Q_Purge(mbox);

      return ERR_OK;

   }

   else

   {

      return SYS_MBOX_EMPTY;

   }

}


               Before going into the explanation, let look at embOS manual to know about OS_Q_GetPtrCond function. It says argument ppData of OS_Q_GetPtrCond should point to the address of the pointer which will be set to the address of the message. As we have seen earlier, we have passed the content of pointer as data in sys_mbox_trypost function, so the OS_Q_GetPtrCond function will set the desPointer with the address of the memory location where the passed data ( which is the pointer content – the address of the original data ) is located. So if you dereference desPointer, it will give the address of the original message, that is after dereference, it has the address of the original message. So we can store this value with *msg. I think with example we will be able to understand this better.

Example:


               In api_msg.c file of lwIP source code ( You can download the source from reference link ), inside the accept_function, there exists one pointer to netconn structure as shown below,

struct netconn *newconn;


              Memory is allocated using netconn_alloc function for this. This pointer is put into mailbox using the below statement.


if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) {


As explained above, function sys_mbox_trypost will put the address stored in the pointer newconn in the message queue, rather than data present in the address pointed by the pointer newconn.


The message queue data is received in netconn_accept function located in api_lib.c file. Here the netconn pointer is declared as shown below,


struct netconn *newconn;


            The data from the mailbox is received by the below statement inside the netconn_accept function,


if (sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {


As you can see here, address of pointer newconn is passed as **msg argument in sys_arch_mbox_fetch function, so the argument **msg should be pointing to the valid data ( which is netconn structure data passed in accept_function function ) after dereferencing it two times. That means after dereferencing it one time ( *msg ) it should point to the address of the netconn structure data. If you see the definition of sys_arch_mbox_tryfetch, after dereference desPointer pointer has the address of the original message ( netconn structure data ), so it just can be assigned to *msg as shown below.


*msg = (void *)(*desPointer);


This much complexity wouldn`t be there if we just put the structure data itself in message queue data instead of pointer to that structure data. But to share the memory created by netconn_alloc, we`re doing this.

  

Other than the above mentioned functions all other function definitions required by lwIP like sys_arch_mbox_fetch, sys_mbox_post , sys_thread_new, etc can be implemented as all of them are similar to this.


Issues:


Okay we`ve implemented all the function definitions required by lwIP. Now let`s look at the issues that could come in our way. There`re many issues that could come. Let`s look at some of them.


One of the main issue that could come is with Cache configuration. You possibly know about cache coherency issues involved here as DMA handles the data transfer of LAN packets, which operates independently of CPU. The simple solution to that is to put DMA Descriptors and buffers in non-cacheable memory region. The procedure for that depends on the compiler used. One of the common mistake we could make here is forgetting to configure the corresponding memory region in linker configuration file. For example in the existing project some name could`ve been used to assign the memory region as non-cacheable and the same would`ve been configured in liker file, so it would work. But in the new project we`re porting, the linker configuration file could have been assigned a different name for non-cacheable memory region or in worst case no memory region at all is assigned as non-cacheable, all of the memory is configured as cacheable. So we`ve to configure the memory region as non-cacheable in both c file as well as in linker configuration file.


               There`s one more configuration related to cache that could cause the issue. As you`re using different project with different RTOS. There`re chances it would`ve different MMU Table set up procedure. There`s high possibility that RTOS initialization function will set up MMU table and in that the memory region you have assigned as non-cacheable could`ve been configured as cache-able. So it`s not enough to check the configuration in DMA descriptor file and in linker configuration file you need to check the MMU table set up in your initialization as well. Because that`s what the final say goes to the controller about the memory region from your source.


Other than this if there are hardware changes, there are possibilities of changes in Phy device address as it depends on the voltage levels on the PHYAD pins. This is the case if there`s small or no change in PHY Device. If the PHY device variant gets drastically changed than you need to take care of the configurations related to that as well, even though IEEE defined registers remain same, there`re good number of PHY registers which are vendor-specific, you need to take care of that as well.


After resolving above issues and many more issues you could face, finally you will be able to communicate with the target. I`ve faced almost all of the issues. Unfortunately for me PHY device was also different one and it had one vendor specific user register and that made me breaking my head for almost an entire day. Hope your porting succeeds. In case you`ve any comments please let me know.


Have a look at below links for some reference:


                         1) lwIP Porting for an OS                          
                         2) lwIP Source
                         3) embOS User Manual




Friday, November 23, 2018

Utilising Function Pointers in Embedded Projects

One of the good thing in C that I didn`t use in my early days of embedded programming was Function pointers ( although it was there in the project thanks to the vendor supplied drivers ). But in many of my projects that helped me to create efficient software. So I decided to write about that in my first technical blog.

There are three major possible use cases for function pointers – Arrays of function pointers (Lookup table), vector table ( type of lookup table ) and callback functions. We will see about each of that, one by one.

Let`s look at lookup table use cases first, to be honest I had been deliberately avoiding the use of the same in my early days as I was not confident due to its cryptic syntax. But as I worked with the projects that had multi-level menu, function pointer was one of the key feature in C that got my attention. I have developed lot of vending products with the multi-level menu over the years, in those projects function pointers not only provided efficiency, but as well as the readability, as using switch case for a menu could go on for more than one page. 

The basic declaration of arrays of function pointers in shown below.

void (*pf[])(void) =
{
fna,
fnb,
fnc,
...
fnz
};

There are few things which could be added to make the above declaration better as shown in below code snippet, which is inspired by nigel jones “Arrays of Pointers to Functions” article (Link is attached in the end of the article).

Static void (* const pf[])(void) =
{
fna,
fnb,
fnc,
...
fnz
};

 Below shows the use case of arrays of function pointers in one of my project. There will be request from another node to my target, for that request target has to invoke the appropriate callback routines. This can be done with the help of switch case, but as the number of request commands increases, the concerning switch case will become too large and will hover across more than one page, results in poor readability and also poor efficiency if the case which positioned bottom of the switch case is executed frequently. Both of this can be addressed ( In cache enabled systems efficiency may differ ) with the help of lookup table (arrays of function pointers) as you can see below.

Static void ( * const RequestHandler[]) ( void ) =
{
vDoTask1,
vDoTask2,
vDoTask3,            
vDoTaskN,
};

void AcceptRequest ( uint8_t u8RequestCommand )
{
if ( u8RequestCommand % 2 )
{
u8RequestCommand = u8RequestCommand / 2;
if ( u8RequestCommand < sizeof( RequestHandler ) / sizeof( *RequestHandler ) )
{
RequestHandler [ u8RequestCommand ]( ) ;
}
}
}

Here all the request commands are odd value and all the reply commands from target are even value, so u8RequestCommand is being divided by 2, after that we ensure the u8RequestCommand is within the limit of look-up table and that value is being used to lookup the target function in lookup table.

There are few things to be noted in this declaration:

1) Prefixing the declaration with static ensures that RequestHandler can`t be accessed outside of the module ( file ). You can put the RequestHandler declaration inside the AcceptRequest function definition as that will even prevent the access outside of the AcceptRequest function.

2)     By testing the u8RequestCommand will ensure only valid command requests will be accepted and we can be rest assured out of bound access will not happen.

3)     By declaring the RequestHandler with the const will make it more likely to be placed in ROM. This will prevent the lookup table being changed at runtime results in more reliable code.

4)     Other things like making the datatype u8RequestCommand unsigned and choosing the smallest datatype possible provide some protection as mentioned in nigel jones article.

In case if the request functions need arguments with different data type, we can change the prototype as shown below,

Static uint8_t ( * const RequestHandler[]) ( void * ) =
{
}

This provides more flexibility to the user as in this case, we just need to typecast the pointer to appropriate types in the member functions as shown below,

Void vDoTask1 ( void * pParamInput )
{
                     Uint32 u32Input = 0;
                     u32Input = * ( ( uint32_t  * ) pParamInput );
                     ….
}
Void vDoTask2 ( void * pParamInput )
{
                     float fInput = 0;
                     fInput = * ( ( float   * ) pParamInput );
                     ….
}

We can even pass the variable number of arguments with the help of structures, but use cases for these are rare.

As far as use of function pointers as a vector table is concerned most of the time we don`t need to change the startup file provided by the vendor so does the vector table, but if we want to port the project from one compiler to another ( e.g: gcc to IAR ) we may need to tweak that. But I prefer having the respective macro definition for each of the handlers I`m using in platform layer file, and by changing that macro we can switch between different compilers by just changing the startup file for the respective compiler. Below shows the vector table for SAM3S controller in startup file given by IAR.

const IntVector __vector_table[] =
{
 { .__ptr = __sfe( "CSTACK" ) },
__iar_program_start,
            NMI_Handler,
            …
            UART0_Handler,   /* 8  UART0 */
            IrqHandlerNotUsed   /* 35 not used */
};

Below shows the vector table for the same SAM3s in in startup file given by Atmel (GCC Based compiler),

IntFunc exception_table[] = {
 /* Configure Initial Stack Pointer, using linker-generated symbols */
 (IntFunc) (&_estack),
Reset_Handler,
NMI_Handler,
USART0_Handler,  /* 14 USART 0 */
Dummy_Handler    /* 35 not used */
};

As there are different handler names used in different compilers for the same IRQ, we need to map this with our handler. One example is shown below,

#define DEBUG_USART_HANDLER USART0_Handler

Void DEBUG_USART_HANDLER ( void )
{
}

Another use of function pointer is through the use of callback functions which is there in almost every embedded project.

You can refer the below article for more information which provides ample information about the use of function pointers.