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