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!