Proton BASIC Compiler - A general discussion on time slicing

  • Pic® Basic

  • A general discussion on time slicing

    By Tim Box


    This text was taken from the Forum post I made.

    Put simply (as I see it) time slicing is when the code sections you want to run takes up large percentage of CPU time.

    If you have a task that has to be performed almost instantly then you would use interrupts, if it's a variety of subroutines that you would like to perform all the time then use time slicing.

    With 1 CPU you will never be able to do true multi tasking, even powerful PC's don't multitask in the true sense, they have multiple threads each doing there own thing. The amount of time the CPU gives each thread depends on the priority of the thread I.e. the percentage of time a thread has the use of the CPU. As PC programs are written to run independently with no regard to other programs the task control is a managed in a way where the code is shut of in the middle of what it was doing and its critical CPU regs saved to be restored once that thread is allocated CPU time again. (Please correct me if I'm wrong on that as I'm not a PC programmer).

    You can do this on a PIC® where there is an interrupt driven task manger saving system regs and jumping to isolated sections of code, letting them have a pre-allocated time slot before interrupting it, saving regs and passing the CPU onto the next bit of code in the list. But in all honesty its not worth it, PIC® have limited resources and the amount of work that is required to manage the whole process means that you cannot just place a whole program onto a task mangers to-do list with out thinking about the consequences to other programs.

    You are far better to write bespoke code for each project, reusing discrete sections of code from one project to the next but not attempting to have a number of individual programs all running at once. I’m sure you knew this already but I like the sound of my own voice, (joke)!

    So back to time slicing, as the point of time slicing is to have several bits of code looking like they are running all at once and we now know that were not into cutting code off in mid sentence, but we still need to have some form of time management. There are a number of ways to do this but as I don’t have the time to list them all, I will talk about the 1 method I use very often.

    Most importantly you have to think about what you want to do and how often you want to do it. Suppose you have 3 tasks that need to be processed: -

    1. The main code menu handling taking user input etc.
    2. Scanning the keyboard
    3. Checking the battery level

    The first one is easy as that’s the main code you just let it run. The second 2 are harder as you don’t need to run them very frequently. Keyboards only needs to be scanned 50 – 100 times a second and the battery checked every 2 seconds.

    So you need to establish a time base with which to clock each task against, a system TICK. I use a 100hz interrupt written in all basic (hardware interrupt not a basic variant). In this I have a number of timers, such as simple counters that when they reach there required count value set a flag and start counting from zero up again and a number of programmable timers where I load a value in, set the start flag they count down every interrupt, when they reach zero they clear the flag.

    So with this system TICK going I have ability to decide when task require processing. With all programs that require a human interface there is a load of hanging around, inevitably you have big pauses or infinite loops that hang there until the user presses a button. What you can do now is use the programmable software timers. On entry to the routine you load the time you require in and set the run flag then in another part of the loop you check for key inputs etc as well as your timer. If the timer runs out you can quit out of that routine turn off a back light etc etc.

    So now you have to handle the other 2 tasks at the right times, this is really easy. What you have is an essential service routine. In my case I call it Essential_Updates, now every routine will call this sub, but as you don’t want to service all the routines in this sub every time so you use the flags generated by the system tick. Say we are going to scan the keyboard every 50hz we have a 2 count counter in the interrupt routine that just sets a flag. We can use this flag to say when to scan. Simply look every time you call the Essential_Updates routine for the flag to be set, if it is you scan the keyboard and then clear flag, it will be set again for another 50hz. Like wise for the 2 second timer the interrupt tick will have a counter going for 200 ticks then set its flag. The battery check routine then looks for the flag to be set runs the bat check code and clears the flag.

    I have to add this is just one method of implementing time slicing and probably not the method you were looking for. For some further reading look at an article I did for the Wiki site on State machines

    And from a further section: -

    Interrupts in PIC® have no inbuilt hierarchy its all on a first come first serve basis. As Johnb said the 18 series have a 2 level system, high (default) and low level. You can assign a level to the interrupt and then after turning the feature on, the cpu will jump to either 8h or 18h depending on the level assigned to the interrupt source.

    So what happens when your assigned interrupt is triggured? Well on a normal interrupt the cpu stops whats it doing and jumps to 4h where usually Proton has some code to save some important registers. Status,Wreg,FSR on 2K and less devices and another variable is saved if the PIC® has more than 2k, this is to indicate the current page you were in on the larger devices. I say normal as on the 18 series it actually does not save any reg's by default at all. This is because the PIC® it's self saves important variables WREG, STATUS & BSR. You can force proton to save them for you as well with 'Context Save'. Normal to restore the vars you would issue 'Context Restore' if you decided to use the PIC® own context saving you just issue RETFI Fast.
    Ok back to what happens on an interrupt, the interrupt is caused when an interrupt flag is set and the interrupt is enabled and the GIE bit set (General interrupt Enable) and the PIE (Peripheral Interrupt Enable) (if required). If you fail to clear the interrupt flag (or disable the interrupt) before you leave the interrupt and issue the ‘Context restore’ command which BTW has retfi (Return From Interrupt) as part of its make up, you will find your self back at the start of the interrupt routine again, as the PIC® just says there an interrupt flag set better go run that code. So a question that might be asked is what happens if you get another interrupt while you’re handling one already? Basically nothing, as on servicing the interrupt routine by jumping to 4h or 8h or even 18h the GIE bit is cleared. There is I hope you have noticed an exception to this rule and that is when you are running dual level interrupts.

    The rules are like this:-
    If you’re in a low interrupt routine and you get another low interrupt nothing happens until you issue RETFI. If you then receive a high interrupt then you jump to the high interrupt routine. Finish that and you go back to the low interrupt code.
    If you are running a High interrupt routine and get another interruption nothing will happen until you leave.

    So reading that you might see a flaw in using the PIC® own registers, that is if you have 2 levels active and use the PIC® regs to save wreg etc in the lower routine, and then get a high level interrupt, again the PIC® will save wreg corrupting the values saved before. So the rule would be if you have a high level interrupt enabled use ‘Context Save’ in the lower routine and then use the PIC® own for the high level code.

    Note Proton provides only one ‘Context Save’ command if you decide to not use the PIC® own system you will have to write your own save and restore routines. Don’t forget to use RETFI Fast to use the PIC® own context restore function.

    Ok hope your following what’s going on. So how do you handle the possibility of running 2 or more interrupts? Well its up to you to check the flags on entry.

    If TMR1INT_FLAG = 1 then goto ……
    If HRSININT_FLAG = 1 then goto….

    Remember you have to clear the interrupt flag before you leave the interrupt!

    Again I hope you have thought about the possibility of getting another interrupt while servicing one already. You could just leave as normal and then be called back to the routine because a flag is still set, but this takes time because you have to restore the systems reg’s leave the interrupt, re-enter, save etc etc. What I recommend is you have your routine arranged like this.

    If TMR1INT_FLAG = 1 then goto TMRCODE
    If HRSININT_FLAG = 1 then goto HRSINCODE

    Context Restore
    Clear TMR1IF
    Goto Int_entry
    Goto Int_entry

    You will see that to leave the interrupt routine you have to first check that there are no more interrupt flags set, and only when they are no more set can you leave. You can enable a small amount of hierarchy by the order in which each flag is checked.

    To finish of this waffle we will now look at what would happen if you were in the HRSINCODE and a timer interrupt occurs. The answer all depends on the way you reload the timer. There are a number of ways you can set up / reload a timer.

    1. You do nothing just let it roll over this is the easiest as it requires no intervention. It is hard to get the period you want as it will only set is intflag when it rolls over to 0. So if your not bothered that it occurs at nice round number periods then that might do for you.

    2. On entry to the int routine, load the timer with a preset figure. This is OK if you know how long it’s taken to get to your code. This though will give you doggy reload times if you are already in an interrupt routine.

    3. You use the CCP regs and timers to automatically load the variables for you. This works well and can be left to free run with no worry about reloading. It’s no use if you don’t have HPWM on he PIC® you are using or you have to use the CCP unit for something else.

    4. You take into account the current count in the timer and use that to work out the reload value. This is what I do in my routines.

    Clear T1CON.0 ' STOP THE TIMER

    So the first thing to do is to stop the timer as you have to do maths on it and that’s not easy if keeps changing. Next we add the current value in the timer to out reload value. Let me explain why. The interrupt is generated when the timer rolls over to 0, it then keeps on ticking 1,2,3,4…… round again to 0. To get the interrupt time we require, we have to load the timer with a value that represents that number of ticks before it rolls over to 0. On my 100khz interrupt using tmr1 which is a 16 bit timer. The sums are 65536 – 10000 (10000 is 1,000,000 cpu instructions per second at 4 mhz / the 100 interrupts per second) Should you wish to change the clock frequency you have re do you maths or calculate it like I do.

    Symbol FUDGE_FACTOR = 7
    Symbol TMR1_VAL =((65536)-(XTAL*2500))+FUDGE_FACTOR

    FUDGE_FACTOR, btw is the number of instructions it take to do the math’s on the timer while you have the timer turned off.

    Now the reason we add the calculated value to the current timer value is because we cannot be certain what the value of timer will be. If we add our reload value to the timer we will be shortening the time it takes to get to rolling over again by exactly the time it took to get to our reload routine.

    So I hope you can see from my very long winded answer is. As long as you use the maths reload routine, you can service any number of other interrupts before you get to the timer reload and you will still be keeping time.