<< Previous | Index | Next >>

> ## 7. The Virtual Driver

> Virtual Driver is the name given to some initialization services and a group of services performed by a periodic interrupt. These services are:

> ### Initialization Services

> - Call `_GLOBAL_INIT()`
> - Initialize the global timer variables
> - Start the Virtual Driver periodic interrupt

> ### Periodic Interrupt Services

> - Decrement software (virtual) watchdog timers
> - Hitting the hardware watchdog timer
> - Increment the global timer variables
> - Drive uC/OS-II preemptive multitasking
> - Drive slice statement preemptive multitasking

> ### 7.1 Default Operation

> The user should be aware that by default the Virtual Driver starts and runs in a Dynamic C program without the user doing anything. This happens because before `main()` is called, a function called `premain()` is called by the Rabbit kernel (BIOS) that actually calls `main()`. Before `premain()` calls `main()`, it calls a function named`VdInit()` that performs the initialization services, including starting the periodic interrupt. If the user were to disable the Virtual Driver by commenting out the call to `VdInit()` in `premain()`, then none of the services performed by the periodic interrupt would be available. Unless the Virtual Driver is incompatible with some very tight timing requirements of a program and none of the services performed by the Virtual Driver are needed, it is recommended that the user not disable it.

> ### 7.2 Calling _GLOBAL_INIT()

> `VdInit()` calls the function chain `_GLOBAL_INIT()` which runs all `#GLOBAL_INIT` sections in a program. `_GLOBAL_INIT()` also initializes all of the CoData structures needed by costatements and cofunctions. If `VdInit()` is not called, users could still use costatements and cofunctions if the call to `VdInit()` was replaced by a call to `_GLOBAL_INIT()`, but the `DelaySec()` and `DelayMs()` functions often used with costatements and cofunctions in `waitfor` statements would not work because those functions depend on timer variables which are maintained by the periodic interrupt.

> ### 7.3 Global Timer Variables

> `SEC_TIMER`, `MS_TIMER` and `TICK_TIMER` are global variables defined as shared `unsigned long`. These variables should never be changed by an application program. Among other things, the TCP/IP stack depends on the validity of the timer variables.

> On initialization, `SEC_TIMER` is synchronized with the real-time clock. The date and time can be accessed more quickly by reading `SEC_TIMER` than by reading the real-time clock.

> The periodic interrupt updates `SEC_TIMER` every second, `MS_TIMER` every millisecond, and `TICK_TIMER` 1024 times per second (the frequency of the periodic interrupt). These variables are used by the `DelaySec`, `DelayMS` and `DelayTicks` functions, but are also convenient for application programs to use for timing purposes.

> #### 7.3.1 Example: Timing Loop

> The following sample shows the use of `MS_TIMER` to measure the execution time in microseconds of a Dynamic C integer add. The work is done in a nodebug function so that debugging does not affect timing.

> ###

>     #define N 10000

>     main(){ timeit(); }

>     nodebug timeit(){
>        unsigned long int T0;
>        float T2,T1;
>        int x,y;
>        int i;

>        T0 = MS_TIMER;
>        for(i=0;i<N;i++) { }

>        // T1 gives empty loop time
>        T1=(MS_TIMER-T0);

>        T0 = MS_TIMER;
>        for(i=0;i<N;i++){ x+y;}

>        // T2 gives test code execution time
>        T2=(MS_TIMER-T0);

>        // subtract empty loop time and convert to time for single pass
>        T2=(T2-T1)/(float)N;

>        // multiply by 1000 to convert milliseconds to microseconds.
>        printf("time to execute test code = %f us\n",T2*1000.0);
>     }

> #### 7.3.2 Example: Delay Loop

> An important detail about `MS_TIMER` is that it overflows ("rolls over") approximately every 49 days, 17 hours. This behavior causes the following delay loop code to fail:

>     /* THIS CODE WILL FAIL!! */
>     endtime = MS_TIMER + delay;
>     while (MS_TIMER < endtime) {
>        //do something
>     }

> If "MS_TIMER + delay" overflows, this returns immediately. The correct way to code the delay loop so that an overflow of `MS_TIMER` does not break it, is this:

>     endtime = MS_TIMER + delay;
>     while ((long)MS_TIMER - endtime < 0) {
>        //do something
>     }

> The interval defined by the subtraction is always correct. This is true because the value of the interval is based on the values of `MS_TIMER` and "endtime" relative to one another, so the actual value of these variable does not matter.

> One way to conceptualize why the second code snippet is always correct is to consider a number circle like the one in Figure 7.1. In this example, delay=5. Notice that the value chosen for `MS_TIMER` will "roll over" but that it is only when `MS_TIMER` equals or is greater than "endtime" that the while loop will evaluate to false.

> Figure 7.1 delay=5

> Another important point to consider is that the interval is cast to a signed number, which means that any number with the high bit set is negative. This is necessary in order for the interval to be less than zero when `MS_TIMER` is a large number.

> ### 7.4 Watchdog Timers

> Watchdog timers limit the amount of time your system will be in an unknown state.

> #### 7.4.1 Hardware Watchdog

> The Rabbit CPU has two built-in hardware watchdog timers, called the watchdog timer (WDT) and the secondary watchdog timer (SWDT). The Virtual Driver hits the watchdog timer (WDT) periodically. The following code fragment could be used to disable this WDT:

>     #asm
>         ld a,0x51
>     ioi ld (WDTTR),a
>         ld a,0x54
>     ioi ld (WDTTR),a
>     #endasm

> However, it is recommended that the watchdog not be disabled. The watchdog prevents the target from entering an endless loop in software due to coding errors or hardware problems. If the Virtual Driver is not used, the user code should periodically call `hitwd()`.

> When debugging a program, if the program is stopped at a breakpoint because the breakpoint was explicitly set, or because the user is single stepping, then the debug kernel hits the hardware watchdog periodically.

> The secondary watchdog timer defaults to disabled. For more information on the hardware watchdogs, please see the user's manual for your Rabbit processor.

> #### 7.4.2 Virtual Watchdogs

> There are 10 virtual WDTs available; they are maintained by the Virtual Driver. Virtual watchdogs, like the hardware watchdog, limit the amount of time a system is in an unknown state. They also narrow down the problem area to assist in debugging.

> The function `VdGetFreeWd(count)` allocates and initializes a virtual watchdog. The return value of this function is the ID of the virtual watchdog. If an attempt is made to allocate more than 10 virtual WDTs, a fatal error occurs. In debug mode, this fatal error will cause the program to return with error code 250. The default run-time error behavior is to reset the board.

> The ID returned by `VdGetFreeWd()` is used as the argument when calling `VdHitWd(ID)` to hit a virtual watchdog or `VdReleaseWd(ID)` to deallocate it.

> The Virtual Driver counts down watchdogs every 62.5 ms. If a virtual watchdog reaches 0, this is fatal error code 247. Once a virtual watchdog is active, it should be reset periodically with a call to `VdHitWd(ID)` to prevent this. If count = 2 for a particular WDT, then `VdHitWd(ID)` will need to be called within 62.5 ms for that WDT. If count = 255, `VdHitWd(ID)` will need to be called within 15.94 seconds.

> The Virtual Driver does not count down any virtual WDTs if the user is debugging with Dynamic C and stopped at a breakpoint.

> ### 7.5 Preemptive Multitasking Drivers

> A simple scheduler for Dynamic C's preemptive slice statement is serviced by the Virtual Driver. The scheduling for µC/OS-II, a more traditional full-featured real-time kernel, is also done by the Virtual Driver.

> These two scheduling methods are mutually exclusive--slicing and µC/OS-II must not be used in the same program.

Dynamic C User's Manual | << Previous | Index | Next>> | rabbit.com