You might have noticed that while we are working on a computer we will be having many processes running at the same time.Actually we have only one processor, and wonder how is this magic happening ? Actually your operating system is time multiplexing your processor ie, each of the process will be allotted a time slot , after which the scheduler of the operating system will find the task which is to be executed, it will run that process for a time quantum . this process is repeated . Which is called a round robin fashion of scheduling the processes . Let us think of task as a small independent code . what we need to do is to make multiple threads for execution. And each of the thread will be executing a task. The advantage is we can concurrently execute the tasks before making multiple threads we should think
about how to make the time quantum for each task . For that we can take the help from the timer . Timer is an inbuilt peripheral device in almost all Micro controllers . A timer is used to get periodic interrupts as configured by the user. for example if the timer is configured to give interrupt in 1 second after each second our process will be interrupted and we can configure the processor in
such a way that the processor will call a function each time when the interrupt occurs . This type of function is called interrupt service routine. Before we transfer control to a thread we will have to save the current state of the currently running task . How can we do it? The current state of the task is contained in each of the processor registers. So by saving the current register of the processor we can save the current tasks state. next we need to execute the next task, for that we need to fill the registers with the save of the other task. A typical task switcher will be like this
about how to make the time quantum for each task . For that we can take the help from the timer . Timer is an inbuilt peripheral device in almost all Micro controllers . A timer is used to get periodic interrupts as configured by the user. for example if the timer is configured to give interrupt in 1 second after each second our process will be interrupted and we can configure the processor in
such a way that the processor will call a function each time when the interrupt occurs . This type of function is called interrupt service routine. Before we transfer control to a thread we will have to save the current state of the currently running task . How can we do it? The current state of the task is contained in each of the processor registers. So by saving the current register of the processor we can save the current tasks state. next we need to execute the next task, for that we need to fill the registers with the save of the other task. A typical task switcher will be like this
fun isr(): save_states_of_current_task(); schedule_next_task(); restore_states_of_scheduled_task();Task Switching in MSP430
I had given an introduction about msp430 in my previous blog. I have implemented a task switching demo code for MSP40
MSP430 is having only a 128 bytes ram so we can only implement task switching up to an extend in this micro controller as we lack memory.
For task switching we will keep a global array of which holds the stack pointer of each task . Also we will keep a variable which points to the current
tasks index. First thing we need to do is to configure timer. Next we need to divide stack for each stack and initialise their stacks . After the
initialisation is done you need to enable interrupts . At this point the timer interrupt will occur after some time. When the timer interrupt occurs,
into the timer isr we first push all the registers onto the stack then we increment the task id and jump to the next task .
.include "msp430g2x31.inc"
#define TASK_ID 0x270
#define TASK_ARRAY 0x214
org 0xf800
;;;start routeine.
start:
mov.w #(WDTPW|WDTHOLD), &WDTCTL ;disable watchdog timer
mov.w #0x280, SP ;initialise stack pointer
eint ;enable interrupt
mov r2,r4 ;copy status register to r4
dint ;disable interrupt
mov.b #255,&P1DIR ;configure all pins of port1 as output
mov.b #0,&P1OUT ;make all the pins off
mov.b #1,&TASK_ID ;initialise current task id as 1
mov.w #(TASSEL_2|MC_1|ID_3), &TACTL;configure timer control register
mov.w #(CCIE), &TACCTL0 ;enable capture compate interrupt.
mov.w #100, &TACCR0 ;set capture compare threshold.
;;;initialise the stack of task1
mov.w #0x258,SP
push #task1
push r4
push 0
push 0
mov.w SP,&TASK_ARRAY
;;;intialise the stack of task2
mov.w #0x230,SP
mov.w SP,&(TASK_ARRAY - 0x02)
eint ;enable interrupt.
jmp task2 ;jump to task1.
gg: jmp gg ;trap in a while loop.
;;;timer isr
isr:
push.w r4 ;push r4 because it was used by other tasks
push.w r7 ;push r7 because it was used by other tasks
mov.b &TASK_ID ,r4 ;copy current task's id to r4
add r4,r4 ;r4 <- r4 * 2
mov.w SP,TASK_ARRAY(r4) ;update the stack pointer of task array
add #1,&TASK_ID ;add 1 to the current task id
mov.b &TASK_ID,r4 ;move current task id to r4
cmp #2,r4 ;compare the current task id with the maximum tasks
jne non_zero
mov.w #0,&TASK_ID ;if they are equal then make task_id to zero
mov.w #0,r4 ;r4 to zero
non_zero:
add r4,r4 ;multiply r4 by 2
mov.w TASK_ARRAY(r4),SP ;load stack pointer with new task's SP
mov.w SP,r5 ;mov sp to r5
add #8,r5 ;add sp with 8
mov.w r5,TASK_ARRAY(r4) ;save back the top of stack to task_array
pop.w r7 ;pop to r7
pop.w r4 ;pop to r4
pop.w r2 ;pop to r2
pop.w r0 ;pop to r0 control gone to next task
;;;task1 to execute.
task1:
xor.b #1,&P1OUT
mov #50000,r4
loop1:
dec r4
jnz loop1
jmp task1
;;;task2 to execute.
task2:
xor.b #(1<<6),&P1OUT
mov #10000,r4
loop2:
dec r4
jnz loop2
jmp task2
;;vector table.
org 0xfffe ;reset vector
dw start
org 0xfff2
dw isr
No comments:
Post a Comment