Sunday, 25 March 2012

POSIX thread programming


Introduction:
Using thread has many benefits. They share the same resources of their calling process (parent process), like global variables, open files and so on. Also they have their own thread ID, stacks, and so on. For more information, please refer to the old article "Multitasking in Embedded Linux" and reference [1]. Here we focus on how to use it.

The simplest thread programming:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *print_message(void *ptr)
{
    char *text;
    text=(char *)ptr;
    printf("%s \n", text);
}

main()
{
    pthread_t thread1, thread2;
    char *str1="I am thread1";
    char *str2="I am thread2";
    int T1ret, T2ret;

    /*Create two threads*/
    T1ret=pthread_create(&thread1, NULL, print_message, (void*) str1);
    T2ret=pthread_create(&thread2, NULL, print_message, (void*) str2);

    /*Wait for both threads to finish*/
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    printf("T1 & T2 return: %d, %d\n", T1ret, T2ret);
}
Here we use pthread_create function to create a thread. There are four arguments in it.
First argument: used to return the thread ID.
Second argument: used to set the attributes of thread, like priority, schedule policy and so on. For detail, please refer to reference [1].
Third argument: assign the thread function which is the name of the function.
Four argument: used to pass value.

Usually at the end of the calling function, we use pthread_join(threadid, NULL) to wait for termination of that thread. This function is very important, because sometimes we need to allocate some memories for threads (refer to reference [2]). After all the threads terminates, we need to free these memories.


How to pass value to thread:

We can only use the fourth argument of pthread_create() to pass value. And it is declared as void *, so it is a totally free local variable. It can be converted to any type of variables by using casting.

Passing of pointer:
This is easy, because the fourth argument is a void pointer itself. So you can use the pointer as fourth argument. In thread, you need to cast it to the  type you like.

Passing of value:
Two ways to do this.
1. In thread_create() function, cast the variable to be passed to void * type. Then in thread, you can cast it to any type you want. For example:
rc=pthread_create(&threads[t], NULL, printhello. (void *)t);
In thread:
void *printhello(void *threadid)
{
    long taskid=(long)threadid;
    ...
}
2. Allocate a or several memories using malloc( ) function (refer to [2]) which return a pointer points to that location. Then you can use it like using pointer we talked in part 1. Here must remember to free the location using free( ) at the end of the function who called malloc( ).
long *taskids[NUM_THREADS];

for(t=0; t<NUM_THREADS; t++)
{
   taskids[t] = (long *) malloc(sizeof(long));
   *taskids[t] = t;
   printf("Creating thread %ld\n", t);
   rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]);
   ...
}

Passing of multiple parameters:
Use structure (struct) to this. Imagine yourself.
struct thread_data{
   int  thread_id;
   int  sum;
   char *message;
};

struct thread_data thread_data_array[NUM_THREADS];

void *PrintHello(void *threadarg)
{
   struct thread_data *my_data;
   ...
   my_data = (struct thread_data *) threadarg;
   taskid = my_data->thread_id;
   sum = my_data->sum;
   hello_msg = my_data->message;
   ...
}

int main (int argc, char *argv[])
{
   ...
   thread_data_array[t].thread_id = t;
   thread_data_array[t].sum = sum;
   thread_data_array[t].message = messages[t];
   rc = pthread_create(&threads[t], NULL, PrintHello, 
        (void *) &thread_data_array[t]);
   ...
}

Synchronization using Mutex:

Initialization of mutex:
pthread_mutex_t mutex1=PTHREAD_MUTEX_INITIALIZER;

Lock a mutex:
pthread_mutex_lock(&mutex1);

Unlock a mutex:
pthread_mutex_unlock(&mutex1);

Destroy a mutex:
pthread_mutex_destroy(&mutex1);

Set the stack size of thread:

pthread_attr_t attr;    // Here we define a attribute variable.

size_t mystacksize;
pthread_attr_getstacksize(&attr, &mystacksize);    // Here we get the stack size of variable attr. Remember it
                                                                             // belongs to variable attr, not the thread who calling it.

pthread_attr_setstacksize(&attr, mystacksize);     // Here we set the stack size of variable attr. Also remember
                                                                           // it belongs to variable attr.

Remember to put variable attr into the second argument of function pthread_create( ), like:
pthread_create(&threads[t], &attr, dowork, (void *) t);

At last, remember to destroy attr:
pthread_attr_destroy(&attr);

This procedure also can be applied to other attributes setting like joinable, scheduling, priority and so on.

References:
[2] malloc - Linux man page, http://linux.die.net/man/3/malloc
[6] pthread_attr_destroy( ) -- Destroy a thread attributes object, http://cursuri.cs.pub.ro/~apc/2003/resources/pthreads/uguide/users-g5.htm

Multitasking in Embedded Linux


Multitasking in Embedded Linux

In the computer field, "task" has the sense of a real-time application, as distinguished from process, which takes up space (memory), and execution time.

 A process, or task, can be seen as a set of allocated resources

Process switching makes big memory consumption. Because, when we created a new process, we need to create a process descriptor and allocate another memory region as its work space.

However, Thread allows you use the same memory in a process to work on, so you don't need to create an extra memory. So real-time linux use just one main process which includes threads to realize multitasking in embedded linux.

But there's more. Threads also happen to be extremely nimble. Compared to a standard fork(), they carry a lot less overhead. The kernel does not need to make a new independent copy of the process memory space, file descriptors, etc. That saves a lot of CPU time, making thread creation ten to a hundred times faster than new process creation. Because of this, you can use a whole bunch of threads and not worry too much about the CPU and memory overhead incurred. You don't have a big CPU hit the way you do with fork(). This means you can generally create threads whenever it makes sense in your program.
Threads differ from traditional multitasking operating system processes in that:
  • processes are typically mean time, while threads exist as subsets of a process
  • processes carry considerably more state information than threads, whereas multiple threads within a process share process state as well as memory and other resources.
  • processes have separate address spaces, whereas threads share their address space.
  • processes interact only through system-provided inter-process communication mechanisms.
  • Context switching between threads in the same process is typically faster than context switching between processes.

Saturday, 24 March 2012

How to create process in Linux


 In Linux, we use fork( ) to create a new process which is actually a copy of the calling process. The new process is a copy (the code after fork( ) function) of its parent  process. So if we do nothing about control flow, the child process will actually do the same thing the parent does.
Here we use the return value of fork( ) to distinguish the child and parent. In fact, the function fork( ) returns two values after it finishes. For the calling process (parent process), it returns the PID of child process in order that the calling process can manage it. However in child process, fork() will return 0. So by this, we can just use a "if(chpid)" to distinguish the twos and make them execute the different sequences.
Another very important thing is the fork only copy the code after the for( ) function. It does not care about the code above is except the definition part of variables.
#include <stdio.h>
#include <sched.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
    pid_t chpid;
    int fd, status, variable=9;
    char ch;
    fd=open("test.txt", O_RDONLY);
    chpid=fork();
    if(chpid!=0)
        wait(&status);
    else
    { /*Executed only by the child*/
        variable=42;
        close(fd);
        printf("The child has changed the variable to: %d\n", variable);
        printf("The child has also closed the file.\n");
        return(0);
    }
    printf("The variable is now: %d\n", variable);
    if(read(fd, &ch, 1)<0)
    {
        perror("READ failed");
        return(1);
    }
    printf("Read from the file: %s\n", &ch);
    return (0);
}
Above is common use case of fork( ). The wait( ) function is used by calling processes to wait for the state changes of their child processes, like resume, termination, restart and so on. For detail, please refer to reference [1].
References:
[1] wait - Linux man page, http://linux.die.net/man/2/wait

Thursday, 22 March 2012

Anonymous pipe and Named pipe


Introduction:
Anonymous pipe and Named pipe are usually used for communication between two different tasks.

Anonymous pipe:
It does not have name, and can be applied in the shell by using ' | '. It is unidirectional.
For example, p1 | p2
Here anonymous pipe usually is used in case of standard output and standard input which are related to the shell. So here you can use anonymous pipe ' | ' directly to transfer data from p1 to p2. So here must remember that the output and input are both for shell which means usually using functions printf() for output and scanf() for input or something like this.

Named pipe:
However, named pipe is related to a dev file which is more flexible. So in this case, you can transfer the data like writing/reading to/from file which is already familiar to you. We can create a pipe by using mkfifo /dev/mypipe where mkfifo is a new command, and in some old version of Linux you may use mknod p /dev/mypipe. But we recommend you use mkfifo /dev/mypipe.

Then we can write our applications like the following to test mypipe:
Read file:
//fifo_read.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

int main(void)
{
FILE *fp;
char buf[20];
/* Create the FIFO if it does not exist*/
umask(0);
mknod("mypipe", S_IFIO|0666, 0);
while(1)
{
fp=fopen("mypipe", "r");
fgets(buf, 20, fp);
printf("We received: %s\n", buf);
fclose(fp);
}
return(0);
}

Write file:
//fifo_write.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
FILE *fp;
if((fp=fopen("pipe", "w"))==NULL)
{
fprintf(stderr, "fopen caused an error\n");
exit(1);
}
fputs(argv[1], fp);
fclose(fp);
return(0);
}

Blocking and nonblocking:
Here we use fopen("mypipe", "r") open mypipe as a normal file which is default for blocking. So the after  writing, the program is blocked and wait for another program to read data. Similarly, after execute the read program, it will be blocked and wait until there is an another program to write in this buffer.

If it is nonblocking, then neither program will be waiting, they will just be executed until the end. But here we cannot use stream functions anymore, because they do not have the option for nonblocking, so you have to use file descriptors which is operated using system call (like operating drivers). So in file descriptor we can open the buffer as nonblocking as following:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int ifd;
...
ifd=open("/dev/mypipe", O_RDONLY | O_NONBLOCK);
...
read(ifd, buf, 20);

The differences between streams and file descriptors:


Warning:
Unless you really really have to use nonblocking buffer, please use stream to do blocking buffer.

Tuesday, 13 March 2012

Principle of Floating point


Introduction:
Compared to fixed point arithmetic, floating point allows us to use both larger scope of integer and fraction in a expression because of the floating exponent. 
A floating point memory block is composed of sign flagexponent(2^e) and mantissa. The format is shown below.
64-bit floating point (double)

32-bit floating point (float)


  • We need the exponent part to be signed, so the compiler will subtract the q with a bias. The calculations are shown below.
e=q-bias

The number the format above represents is:
Here you must remember it is 1.M which is we call "significant figure" (有效数字). So when you do the some arithmetics, you have to ensure you shif the number to keep there is a "1" in the left of ".".

Precision of floating point:

Because the significant digits(有效数字) only have sizeof(M) bits, so the precision of floating point is only 2^(-sizeof(M)-1) (here we have a virtual bit). When a number is smaller than this precision, it will not be represented any more.

On the other hand, if the number exceeds 2^(sizeof(M)+1)its precision cannot be guaranteed anymore, which is also because the significant digits(有效数字) have limited bits (sizeof(M)). If a number exceeds the those significant digits, there is no enough room for the extra precision which will be missed. An example about this is shown below.

     #include <stdio.h>


     main()
     {
         int x;
         float y;

         x=1;
         while (x>0)
         {
             y=x+1;
             x=y;
         }

     }

This program will never end, because x will never overflow. When x exceeds the significant 
digits(有效数字), y cannot increase anymore. So this program will be stuck at x=16777216 (2^24).

The arithmetic (addition & subtraction) of floating point:
Here we talk about how to do addition and subtraction of floating point. As we know when we want dto do addition or subtraction of numbers with exponent, we have to align the exponent first. The floating point calculation needs the same operation.

So here we use shift on mantissa and increase on exponent to solve this problem. We start from the smaller number. When we shift the mantissa 1 bit to left, we increase the exponent by 1, until the exponent is equal to the bigger number. 

For example, we try to add 100.0 with 0.25 whose formats are "0 10000101 10010000000000000000000and "0 01111101 00000000000000000000000" respectively. 

Then we start to shift 0.25:

0 01111101 00000000000000000000000 (original number)
0 01111110 10000000000000000000000 (right shift 1)
0 01111111 01000000000000000000000 (right shift 2)
0 10000000 00100000000000000000000 (right shift 3)
0 10000001 00010000000000000000000 (right shift 4)
0 10000010 00001000000000000000000 (right shift 5)
0 10000011 00000100000000000000000 (right shift 6)
0 10000100 00000010000000000000000 (right shift 7)
0 10000101 00000001000000000000000 (right shift 8)

Then we add 0 10000101 10010000000000000000000 with 0 10000101 00000001000000000000000.

                 0 10000101 10010000000000000000000
+ 0 10000101 00000001000000000000000
-----------------------------------------------------------
                 0 10000101 10010001000000000000000
                                           ||
                                     100.25



References:
[1] Chapter 7 -- floating point arithmetic, http://pages.cs.wisc.edu/~smoler/x86text/lect.notes/arith.flpt.html

Magic of union


"union" is very useful C type which allows you to share the same memory block when using different types of variables. Although, the types of variable is different, but the content of the memory they share is the same. So by this way you can "read" the content by using shift and "&" operator if the content is formated and assigned by compiler.

Anyway, just remember "union" makes its members share the same memory block, and the size of memory block depends on the longest member.

The following is an example to show how to use "union" to transfer floating point to binary.

#include <stdio.h>

union ufloat /*here we share the unsigned integer and float variable in the same location*/
{
     float f;
     unsigned int i;
};
void print_float(float a)
{
     char bin[32]={0};
     char index=0;
     int tmp;
     ufloat u1;

     u1.f=a;
     tmp = u1.i; /*here we get the decimal conversion of float variable*/
    
     for(unsigned int j=1;j>0;j<<=1)
     bin[index++]=(tmp&j)?1:0; /*here we convert the decimal number into binary*/
     printf("output: %d ", bin[31]);
     for(char j=30;j>=0;j--)
     {
          printf("%d",bin[j]);
          if(j==27 || j==23 || j==19 || j==15
             || j==11 || j==7 || j==3)
          printf(" ");
     }
     printf("\n");
}
int main()
{
     float x=1.5;
     print_float(x);
     return 0;
}

Results:
output: 0 0111 1111 1000 0000 0000 0000 0000 000

Thursday, 8 March 2012

Digital system design flow




Specification:
We need to define the requirements and implementation constraints of our system like we design a software architecture. 

High-Level Design:
We need to define the top-level architecture, which includes the modules needed in the design and how they communicate with each other.

Low-Level Design:
Each module in this stage should be implemented using simple design components like memories, regsisters, state machines, etc.

HDL Coding:
Here we need to use HDL to write code to implement the low-level design. Here HDL code must be synthesisable.

Functional Simulation:
Here we use the software simulator to test if the HDL design does the right things which are required by the specification.

Synthesis:
After the simulation verified the design complete the requirement by specification, then map the HDL design to the target technology primitives, initial timing info is obtained.

Placement & Routing:
Here all the primitives in the synthesised netlist are mapped to a location on the target. Then connect the inputs and outputs to pins of FPGA. (passing of signals, clock trees are generated detailed timing information is obtained)

Tming Simulation:
After synthesis, you need to simulate the target again to chech that if the whole design meets the timing constraints set in the specification.

Fabrication:
For FPGA design, now all the works have been done. For an ASIC, mask sets are produced and silicon wafers are etched.

Post Silicon Validation:
After the chip is manufactured, we are supposed to test it in field conditions again. If there are bugs here, then all the chips become rubbish.

Difference between "docker stop" and "docker kill"

 To stop a running container, you can use either "docker stop" or "docker kill" command to do so. Although it seems doin...