Member of:

Real-time timer with Delphi in kernel mode

With the »RealTime Suite« you can execute parts of the application code on kernel level (circle 0 of the CPU). With the possibility of kernel programming you can for example program an exact timer, which can be called directly on kernel level. A C or C++ compiler is suitable for kernel mode (e.g. Borland C++Builder or Microsoft Visual C++) - but also you can use the common Pascal programming environment Delphi of Borland (version from 2 to 7 resp. from 8 in native mode), for creating ring circle code for kernel level. You don't need assembler programming.

In the example a timer function with a frequency of 10 kHz shall be called, which informs the application after 10000 calls with help of an event.

Therefore the product range Kithara »RealTime Suite« provides all required mechanisms. For example, programming of kernel functions in Delphi source code, provision of shared memory for data exchange between the callback function and the other application parts and also generating and setting of events from the timer function. Furthermore threads can be created in an easy way, in which you can wait for the arrival of the agreed events, without having to block the application.

In the following example the different steps will be clarified, but you will miss any error evaluation here. A shared memory generated record, which includes only one counter and the event handle, serves for the data exchange between the kernel and the user level:

type
Data = packed record
  count:  UInt;
  hEvent: Handle;
end;
PData = ^Data;

Furthermore the following specific variables will be defined:

var
pAppPtr: PData;       // Shared Memory (Ring 3)
pSysPtr: PData;       // Shared Memory (Ring 0)
hCallBack: Handle;    // Callback-Handle
hTimer: Handle;       // Timer-Handle
period: UInt;         // Timer-Periode in 100-ns

The first two pointers serve for the administration of the shared memory, because the system and the user address space differ. This shared memory is required, because usual variables of the application are not accessible from kernel level. Also you cannot access kernel memory from the application. The demanded length is the size of the record 'Data':

err := KS_createSharedMem(@pAppPtr, @pSysPtr, 'MyShared', SizeOf(Data), 0);

You can directly access the elements of the record with the application pointer:

pAppPtr^.count := 0;

In order to advise the application a specific event will be created, because usual Win32 events are not valid on the kernel level. The resulting event handle will be packed in the appropriate variable of the record 'Data':

err := KS_createEvent(@pAppPtr^.hEvent, 'MyEventName', 0);

The timer period shall be 100 μs, according to the wanted frequency of 10 kHz. Because all timing values in the toolkit must be specified in 100 ns units, we set the period to the value of 1000:

period := 1000;

Now we turn our attention to the callback function, which will execute on the kernel level with a frequency of 10 kHz. For simplicity it shall only raise a counter variable and additionally after 10000 calls - nearly a second - set an agreed event object for signalling:

function callback(
  var data: Data; var context: TimerUserContext):
Error; stdcall;
begin
  data.count := data.count + 1;

  if data.count > 10000 then
    KS_setEvent(data.hEvent);

  callback := KS_OK;
end;

Please note, that the function normally has to return the value 0 (here defined as KS_OK). With other values you can signal error terms of any kind and the whole process can be cancelled.

First we create a handle for the callback function 'callback'. As a reference parameter to the function the pointer of the shared memory will be specified, which is provided for access from the kernel level ('pSysPtr').

err := KS_createCallBack(@hCallback, @callback, pSysPtr, KSF_ASYNC_EXEC);

Now we have to activate the real-time engine first. Therefore we have to calibrate and adjust the hardware timer to 10 kHz:

err := KS_calibrateMachineTime(0, 0);
err := KS_setTimerResolution(period, 0);

At last we start the timer:

err := KS_createTimer(@hTimer, period, hCallBack, KSF_REALTIME_EXEC);

The timer is running now, so the callback function described above, will be called with a frequency of 10 kHz on the kernel level. Now we want to create a specific thread, which waits for the event. Find here the thread function, in which we first of all set a relatively high priority:

function thread(var data: Data): Error; stdcall;
begin
  KS_setThreadPrio(28);
  KS_waitForEvent(data.hEvent, 0, timeout);
  ...  // Aktionen
  thread := KS_OK;
end;

The timeout variable is a cancel criterion to avoid an infinite long thread blocking in case of error. In case of a timeout situation the function KS_waitForEvent would return the error code KSERROR_WAIT_TIMEOUT.

The system address 'pSysPtr' of the shared data area is given to the ring 0 timer, While the thread receives the appropriate application address 'pAppPtr':

err := KS_createThread(@thread, pAppPtr, nil);

After the correct execution of the function the thread starts and waits for the signalling from the kernel level.

Conclusion

Easy programming of fast circle 0 functions for the kernel level is also possible with Delphi and without assembler code. So, also with Delphi hardware dependent and time critical industrial applications can be realized.

Go back