Testing

    This code is written in a short time by one man, it is by no means error free.
    However, some test cases were implemented to test the system and the tests were passed.
    Tasks test the inter process communication (IPC)- kind of embedded system style.

    The main test:

        There are 3 tasks circulating message using receive/send system calls AND shared memory. The message is incremented by 2 every round. Eventually (after 0x100 rounds), the highest priority task of these 3 tasks 1,2,3 go to sleep (delay_time system call). Then a lower priority task (4) starts running. It counts the sum of first 0x100 integers using a recursive subroutine tens or millions times (depending on test purpose, see later) and compares the result got to the right value 0x8080 incrementing either variable correct or incorrect. Some time also this task go to sleep giving turn to even less  privileged tasks sending messages to each other(or communication with semaphores depending on the init_task). When delay_time -system call returns with the highest priority task it starts the circulation again and low priority tasks wait their own turn.

    Init_task forks other task to run

   For main makefile ./buid/Makefile

   we have:

   ....................

   ifdef TEST5
   SUBDIRS       = $(KDIRS) ../lib ../task52 
   LDS        = 4_lds.lds

........................

 and comman to build is

 make test5=1 wbs


____________________________________________
    void init_task (void) {
       if ((fork_Hannu())==0x1944)        
           if ((fork_Hannu()) ==0x1944) 
                if ((fork_Hannu()) ==0x1944)
                    if ((fork_Hannu()) ==0x1944)
                        if ((fork_Hannu()) ==0x1944)               
                    T4_task(); // task1 -priority 1
                        else
                              T1_task(); // task6-priority 6  - THIS IS LAST PRIORITY SEND/RECEIVE TEST
                     else 
                         T2_task();  //
priority 5-IF THE NAMES ARE T1S-task() (prior 5) and T2_task  (prior 6)--> semaphore communictaion
               else                                  
                T7_task();  // 
priority 4
           else                    
               T6_task(); // priority 3
         else                    
        T5_task();    //  priority 2                     

    }

      ______________________________________________

    For TEST4=1 the init_task is:


    void init_task (void) {
       if ((fork_Hannu())==0x1944)                            
           T1S_task();   //Semaphore test                   
       else                    
          T2S_task();                   
    }
      ______________________________________________

  and main Makefile used part is:

.....................

SUBDIRS       = $(KDIRS) ../lib ../task41 ../task42
LDS        = 2_lds.lds

............................


This gives a more organized test example  (tasks in separate directories).

  
    The data of the tasks started by init_task are in the same “pool” (common data and code section).  A protection problem? Not so bad, the  first time a task refers to a data it gets it's own private physical copy (demand paging). 

The current system supports only following:


Of course, more complete protection is easily created:

We could not have only CS_M, DS_M, DE_M in the linker script but variables/task CS1, CS2,...., code start 1 ,code start2 .....(exercise?)

Also it would easy to add some protection the using of the  fork-call.

    A rough pseudo codes of the test tasks:

    task1
    {
    create shared memory;
    open shared memory;
    loop
    {
     message= receive(task2);
     put the message got, incremented by 1 to the shared memory
     if( already 100 times)
          delay_time(value);
     ack=send(task3,0x1234567);//To wake up , real message in shm!
         }
    }

    task2
     {
      loop
      {      
        message= receive_call(task3);
        if(message>0) {// If not error return (time-out) we increment the counte      
            increment message counter;
            ack = send_call(task1,message);
        }
      }
    }

    task3
    {  
       shm_open_call;
       ack = send_call(task2,1);// Start looping 3->2->1->3->2->1->...  
      loop {
        //*ptr1=1; //Memory protection works- makes Hal t
           message= receive_call(task1);//WAKING UP MESSAGE
           get message from shared memory;
          ack = send_call(task2,++got message);
        }
      }
    }

    Task4

    long  T7_recurs(long);
    void  T7_sub(long);
    void  T7_task(void) {
      int i1,j1,s; 
      while(1) {   
         for(i1=0;i1<LOOPCOUNT1 ;i1++)// Just loading the system with some stupid work
           for(j1=0;j1<LOOPCOUNT2 ;j1++)
           {
        T7_sum  = T7_recurs(DEPTH_OF_RECURSION);// Depth of recursion 150 levels
        if(T7_sum ==0x8080)correct++;
        else  incorrect++;
           }
         __asm__("fadd.d  f8=f8,f1"); //spill/fill testing of float
         if (++T7_count==0x1)
          {
             T7_sub(0x5222);
             T7_count=0;  
         }
      }
    }

    long T7_recurs(long n) {
      if ( n > 1)
           return(n + T7_recurs (n-1));
       else      
      return 1; 
    }

    void T7_sub(long delay) {
    delay_time_call(delay);
    }

    We have multiple way to use task4. If task 5 and 6 are left away, we test the situation “kernel idle loop”, all tasks sleeping (when task 4 sleeps at the same time as tasks 1, 2, 3). If we put task4 loop very long time, we can test that system works even a task is interrupted at a arbitrary place (higher priority wakes up many times before task4 sleeps).
    We can see the correctness from the counter “correct” (sure, if we calculate sum of first 0x100 integers by a recursive subprogram and the result is correct 10000000 times and incorrect 0 times it looks to work.
    We can use also IDLE task to test backing store switch to work for sure. The “idle” task only set ALL stacked registers to a certain odd value -other tasks still working well.

__________________________________________


NEWEST VERSION:

Based on the previous version, but now we have round robin set of tasks also.

The init_task example:

void init_task (void) {
     if ((fork_Hannu())==0x1944)        
         if ((fork_Hannu()) ==0x1944)
             if ((fork_Hannu()) ==0x1944) 
                if ((fork_Hannu()) ==0x1944)
                    if ((fork_Hannu()) ==0x1944)
                       if ((fork_Hannu()) ==0x1944)
                            if ((fork_Hannu()) ==0x1944)
                            if ((fork_Hannu()) ==0x1944)
                                if ((fork_Hannu()) ==0x1944)  
                                         T4_task(); // 1.
                                     else
                                      T7_task(); // 10.
                                   else
                                     T7_task(); // 9.
                               else 
                                T7_task();  //8.
                           else
                             T7_task(); // 7.
                        else
                         T1_task(); // 6.
                 else 
                     T2_task();  // 5.
              else                                  
               T7_task();  // 4 <------------------round robin from here to up
           else                    
           T6_task();     //3.
      else                    
       T5_task();    //2                     
}


There is in the main makefile: ./build/Makefile an exported compiler flag ROUND_ROBIN_LIMIT. It tells from which point (to the end)

tasks are treated as round robin scheduled. (In example case from 4), so only T4,T5 and T6 are priority based.



 

    A linker script example:


    …...........................
     .task_code  addr  :
         {
        __CS = .;
           init_task.o(.text);
           T5_task.o(.text)
           T5_*(.text)
           T7*(.text)
           T4*(.text)
           T6*(.text)
           T1*(.text)
           T2*(.text)
         }       
      .task_data  ALIGN(0x1000)  :
         {
        __DS = .;
           init*(*)
           T5*(*)
           T4*(*)
           T6*(*)
           T7*(*)
           T1*(*)
           T2*(*)   
        }
        . = . + 0x3000  ;   
        .dummy_section  ALIGN(0x1000) :
        {
         __DE = . ;

        }
    …................


    A test example:

    (1) ski wbs
    (2) *bs T7_sub
    (3) *run
    Interrupting simulation
    (4)
    1992613072978 insts (26588611 faults), 36366.53 sec, 54792506 i/s, 654894440506 cycles, 3.04 ipc
    (5)

    * dj 4000000000056090
    (6)
    *
    (7)
    (8)

    Data                                                                                                                                                                      
    .............
    000000000056090 0000001000000000 0000000000000000 0000000000000000 0000000000000000
    40000000000560b0 0000000000008080
    ….....................


    ( from map file ./build/map:
    .sbss         
                0x4000000000056090                correct
                0x0000000000056098                incorrect
                0x00000000000560a0                T7_count               
                ….................
       )


    OBS!
    Because we test IPC, the system is fairly complicated. For example: if we set the calculation task to do a heavy work, lower priority tasks never activate. High priority tasks wake up before calculation is finished!