|
|
|
|
|
This page presents an overview of the API (Application Programming Interface) of AVIX. In detail the API can be found in the AVIX User Manual. The contents of this page is meant to present an overview and discuss the special attributes of the AVIX API.
|
|
|
|
|
|
The AVIX API offers a number of properties not found in many other real time kernels that make working with AVIX easier and more error prone. These properties are:
- Pure function call based: The complete API of AVIX is based on a number of function calls. Never should the user of AVIX have to access AVIX internal data structures as is the case with many competing products. Although for its internal working AVIX uses a number of data structures, these are all internal and fully abstracted by the function interface offered. This leads to a much easier to use interface and thus introduces less errors.
- Clear Application Programming Interface: The API of AVIX offers a number of functions to manipulate kernel objects as event groups, timers etc. The approach followed by many competing products is to give the API functions a large number of parameter to tune the functions behavior. What is often observed here is that in many situations don’t care values are needed for certain parameters. This often makes it complicated to clearly understand what these functions actually do. The design approach followed by AVIX is that for a certain function call, every parameter should be valid and have a clear meaning in the context of the function called. No use is made of don’t care values, instead if this is applicable another function is specified with a limited number of parameters. An example are the functions avixEventGroup_Wait and avixEventGroupThread_Wait. The first waits for the desired flags in a global event group, the second waits for the desired flags in the private event group of the calling thread. This could also be done with one function where for the second case, instead of specifying an id of an event group, some don’t care value was supplied. By modeling this as two separate functions, it is more clear what is happening and the chance on errors is minimized. Furthermore, AVIX uses a very clear naming schema for its functions. Every function begins with avix<object>, where object is the kernel object the function is working on. All timer functions start with avixTimer. Next, separated with an underscore character (_) the function is expressed such that it is clear what the function is doing. Although this can lead to quite long function names, it minimizes the chance on mistakes. Finally, when arguments can have two possible values, no use is made of booleans but instead a typesafe enumeration is used, clearly stating what it’s meaning is.
- Highly type safe: Where possible the API of AVIX is type safe. This means that at a number of places when arguments are passed that are of the wrong type, this is detected compile time instead of runtime. The advantage is that errors are detected earlier and that runtime checks can be left behind thereby not cluttering the code. As a whole this contributes to a higher quality of the product based on AVIX. An example of this is the type of object id’s. Every kernel object (thread, mutex, timer, ...) is identified by an id. When the kernel object is created, the create function returns an id for the kernel object to the calling application. When an operation is required on a kernel object, its id is used to identify which kernel object the operation is to be performed on. This is illustrated with an example. In the code section below you see a call to create a mutex object, followed by the call to lock the mutex. As you can see, the id returned from the create call is passed as an argument to the lock call.
tavixMutex mutexId = avixMutex_Create(NULL, FALSE); ...; avixMutex_Lock(mutexId);
Suppose the mutex id is passed to an API function for a thread, which expects an id of type tavixThreadId instead of tavixMutexId, this is detected by the compiler so the programmer can fix the error before the program is actually running which would be much harder and take more time. This is shown in the code snippet below:
tavixMutexId mutexId = avixMutex_Create(NULL, FALSE); ...; avixThread_Resume(mutexId); // Thread call with id of incorrect type error: incompatible type for argument 1 of `avixThreadResume’
Centralized error handling: Error handling of AVIX is centralized meaning that a runtime error is not returned to the calling function. When a call is made to an AVIX function and the call returns, the call can be assumed to have succeeded. In case an error is detected the call does not return but instead a central error handler is called with an identification of the error made. The philosophy behind this is that runtime errors are always based on some programming error and programming errors should not be dealt with runtime since whatever way you look at it, a programming error leaves the system in an non-deterministic state. The effect of this is that the application code becomes much cleaner since not every call is surrounded by extra code to deal with all possible types of error. This in turn leads to cleaner application code which is easier to understand and leads to less errors.
Both basic and extended event flag and timers model: AVIX offers event flags as separate kernel objects. These event flags can be used for inter-thread communication by letting one or more threads wait for a certain group of flags while other threads can set these flags. This feature is also present in a basic form. To use event flags, it is not required to make separate event flag kernel objects. Every thread has an implicit event flag object that can be used by the owning thread to wait for certain flags while these flags can be set by any other thread. This not only offers a simplified programming model but also uses less system resources than separate event flag objects. Likewise with timers. Timers can be created as separate kernel objects where multiple threads can wait for. Here too, every thread has an implicit timer that can be used by the owning thread to wait for without the need to create a separate timer object.
|
|
|
|
|
|
AVIX offers a large number of custom types. Every AVIX type is identified ‘tavix<type details>’ so there can be no misunderstanding what the type is meant to be used for. The following types are specified by AVIX:
|
|
Type
|
Description
|
|
tavixDIH
|
Function pointer type. C functions used as a DIH must adhere to this signature.
|
|
tavixErrorCode
|
Enum type with all AVIX error codes
|
|
tavixEventFlags
|
Type specifying a 16 bit field of flags used with the event group functions.
|
|
tavixEventGroupCombine
|
Type used to identify when waiting for event flags what the type of operation is when the desired bitmask is found. Possible values:
- AVIX_EVENT_GROUP_ALL: When waiting for multiple bits, all specified bits must be set for the wait function to succeed (comparable to a logical AND).
- AVIX_EVENT_GROUP_ANY: When waiting for multiple bits, at least one of the specified bits must be set for the function to succeed (comparable to a logical OR).
|
|
tavixEventGroupOperation
|
Type used to identify the desired operation with one of the EventGroup class _Change operations. Possible values:
- AVIX_EVENT_GROUP_CLEAR: The bits specified in argument eventFlags in the _Change operations are cleared.
- AVIX_EVENT_GROUP_SET: The bits specified in argument eventFlags in the _Change operations are set.
- AVIX_EVENT_GROUP_TOGGLE:The bits specified in argument eventFlags in the _Change operations are toggled.
|
|
tavixEventId
|
Type used to identify an event kernel object
|
|
tavixKernelObjectId
|
Generic kernel object id used to put all possible kernel object types in a message
|
|
tavixKernelObjectIdp
|
Generic kernel object id pointer used to get all possible kerne object id types from a message
|
|
tavixKernelObjectName
|
Type used to pass a kernel object name to a Create or a Get function
|
|
tavixMemPoolId
|
Type used to identify a memory pool
|
|
tavixMsgId
|
Type used to identify a message object
|
|
tavixMsgType
|
Type used to identify message content
|
|
tavixMutexId
|
Type used to identify a mutex kernel object
|
|
tavixPipeCallback
|
Type used for a callback function used from a pipe. Pipe callback functions must adhere to this signature.
|
|
tavixPipeEvent
|
Event type used with pipe callback to specify reason of call
|
|
tavixPipeId
|
Type used to identify a pipe kernel object.
|
|
tavixPriority
|
Type used for thread priority.
|
|
tavixSemaphoreId
|
Type used to identify a semaphore kernel object
|
|
tavixThreadFuncType
|
Function pointer type. C functions that are started as a thread must adhere to this signature.
|
|
tavixThreadId
|
Type used to identify a thread kernel object
|
|
tavixThreadTracePort
|
Safe enum type identifying the trace ports that can be used for thread activation tracing
|
|
tavixTimerId
|
Type used to identify a timer kernel object
|
|
tavixTimerTick
|
Type used for the basic timer ticks used with time related functions.
|
|
|
|
|
|
|
The following functions are offered, categorized by functional area:
|
|
|
|
|
|
Integration of Interrupt Service Routines with other functionality offered by AVIX is done through a very limited number of functions. The interrupt architecture of AVIX is based on a three layer model of Interrupt Service Routine, Deferred Interrupt Handler and threads. Interrupt Service Routines are only allowed to call a very limited number of functions that are presented here.
|
|
Function
|
Description
|
|
avixDIH_Queue
|
Register a Deferred Interrupt Handler to be activated when ISR’s are no longer active
|
|
avixPipe_ReadFromISR
|
See Pipe API
|
|
avixPipe_WriteFromISR
|
See Pipe API
|
|
avixPipe_StopDeviceFromISR
|
See Pipe API
|
|
avixMemPool_AllocateFromISR
|
See Memory Pool API
|
|
avixMemPool_FreeFromISR
|
See Memory Pool API
|
|
|
|
|
|
|
The basic entity of AVIX is formed by a thread. Essentially a thread is a function that runs under control of AVIX, as specified by the parameters the user defined when creating it. Threads are the only active entities which means that threads consume CPU cycles. During their execution, they make use of AVIX functions to manipulate other kernel objects, communicate and synchronize.
|
|
Function
|
Description
|
|
avixThread_Create
|
Create a new thread
|
|
avixThread_Get
|
Get a thread id of an existing thread and wait if not yet existent
|
|
avixThread_Suspend
|
Suspend the calling thread <To be impl.>
|
|
avixThread_Resume
|
Resume a suspended thread
|
|
avixThread_Relinguish
|
Abandon current round robin time slice
|
|
avixThread_GetIdCurrent
|
Get id of current thread (myself)
|
|
avixThread_SetTracePort
|
Assign a trace port to a thread
|
|
avixThread_SetTracePortAndResume
|
Assign a trace port to a thread and resume the thread
|
|
avixThread_Sleep
|
Wait for specified time. During this time the thread is inactive
|
|
avixThread_ArmTimeOut
|
Set a time-out value for the following potential blocking function
|
|
avixThread_TimeOutOccured
|
Test if the preceding potential blocking function timed out
|
|
|
|
|
|
|
Mutexes are binary semaphores. They are used to guard critical sections where data is being manipulated for which it is essential that this manipulation is done uninterrupted. Mutexes are the only AVIX kernel objects with ownership. When a mutex is locked by a thread, there exists a relation between the mutex and the thread. A locked mutex can therefore only be unlocked by the owning thread.
|
|
Function
|
Description
|
|
avixMutex_Create
|
Create a new mutex
|
|
avixMutex_Get
|
Get a mutex id of an existing mutex and wait if not yet existent
|
|
avixMutex_Lock
|
Lock a mutex and wait if not available
|
|
avixMutex_Unlock
|
unlock a mutex
|
|
|
|
|
|
|
Semaphores are kernel objects having a numeric value. They can be successfully locked as many times as the count of the semaphore allows. After as many locks are taken as the initial count is set, locks will result in the calling thread to be blocked until the semaphore is unlocked again by another thread.
|
|
Function
|
Description
|
|
avixSemaphore_Create
|
Create a new semaphore
|
|
avixSemaphore_Get
|
Get a semaphore id of an existing semaphore and wait if not yet existent
|
|
avixSemaphore_Lock
|
Lock a semaphore and wait if not available
|
|
avixSemaphore_Unlock
|
unlock a semaphore
|
|
|
|
|
|
|
Event groups are kernel objects maintaining binary flags that can be set, cleared toggled and waited for in any possible combination. As such they can be used as an inter-thread communication mechanism. Besided explicitly accessible, event group flags can also be manipulated by timers expiring, messages being send to threads and pipes reaching a specified level of filling or a specified level of empty space.
|
|
Function
|
Description
|
|
avixEventGroup_Create
|
Create a new event group object
|
|
avixEventGroup_Get
|
Get an event group id of an existing event group and wait if not yet existent
|
|
avixEventGroup_Change
|
Clear, set or toggle one or more flags of an event group
|
|
avixEventGroupThread_Change
|
Clear, set or toggle one or more flags of a thread implicit event group
|
|
avixEventGroup_Wait
|
Wait for a specified number of event group flags to be or become set
|
|
avixEventGroupThread_Wait
|
Wait for a specified number of thread implicit event group flags to become set
|
|
|
|
|
|
|
AVIX supports timers as separate kernel objects. Every thread contains an implicit timer with limited functionality. This thread implicit timer functionality is accessible through function avixThread_Sleep.
|
|
Function
|
Description
|
|
avixTimer_Create
|
Create a new timer object
|
|
avixTimer_Get
|
Get an id of an existing timer object and wait if not yet existent
|
|
avixTimer_Set
|
Set the properties of a timer
|
|
avixTimer_Start
|
Start a timer
|
|
avixTimer_Stop
|
Stop a timer
|
|
avixTimer_Wait
|
Wait for a timer to expire
|
|
avixTimer_ConnectEventGroup
|
Connect an event group to a timer for clearing, setting or toggling flags when the timer expires.
|
|
avixTimer_ConnectEventGroupThread
|
Connect a thread implicit event group to a timer for clearing, setting or toggling flags when the timer expires.
|
|
|
|
|
|
|
Messages are used to communicate small blocks of information between threads. AVIX implements a client-server model meaning that a message is sent to a specific thread. There is no such mechanism as a mailbox exchange.
|
|
Function
|
Description
|
|
avixMsg_Allocate
|
Allocate a new message block from free memory or from the message pool
|
|
avixMsg_Reuse
|
Prepare a message for reuse after having received id
|
|
avixMsg_Free
|
Return a message to the message pool
|
|
avixMsg_GetType
|
Get the type of a received message
|
|
avixMsg_PutChar
|
Put a single byte value in the message body
|
|
avixMsg_PutShort
|
Put a double byte value in the message body
|
|
avixMsg_PutInt
|
Put a value in the message body whose size depends on the type of controller used (16-bit or 32-bit)
|
|
avixMsg_PutLong
|
Put a long (4 byte) value in the message body
|
|
avixMsg_PutPtr
|
Put a pointer in the message body
|
|
avixMsg_PutKernelObjectId
|
Put a kernel object id of any desired type in the message body
|
|
avixMsg_PutIndirect
|
Put free format data in the mesage body.
|
|
avixMsg_GetChar
|
Get a single byte value from the message body
|
|
avixMsg_GetShort
|
Get a double byte value from the message body
|
|
avixMsg_GetInt
|
Get a value from the message body whose size depends on the type of controller (16-bit or 32-bit)
|
|
avixMsg_GetLong
|
Get a long (4 byte) value from the message body
|
|
avixMsg_GetPtr
|
Get a pointer from the message body
|
|
avixMsg_GetKernelObjectId
|
Get a kernel object id of any desired type from the message body
|
|
avixMsg_GetIndirect
|
Get free format data from the message body
|
|
avixMsgQThread_GetMsgCount
|
Return number of messages in the threads message queue <to be impl.>
|
|
avixMsgQThread_Send
|
Send a message to a designated thread
|
|
avixMsgQThread_Reply
|
Send the message back to its originator
|
|
avixMsgQThread_Receive
|
Wait for a message to be received
|
|
avixMsgQThread_ConnectEventGroup
|
Specify flags in an event group to be set when messages are present
|
|
avixMsgQThread_ConnectEventGroupThread
|
Specify flags in a thread event group to be set when messages are present
|
|
|
|
|
|
|
Basically intended to be used between threads and interrupt service routines (ISR’s), AVIX offers pipes. Pipes are First-In-First-Out data buffers. AVIX pipes offer a mechanism that aids in controlling the device related to the Interrupt Service Routine on one side of the pipe. Besides being used between a thread and an ISR, pipes can also be used between two threads where the exchange of data forms the basis for synchronizing the threads.
|
|
Function
|
Description
|
|
avixPipe_Create
|
Create a new pipe object
|
|
avixPipe_Get
|
Get an id of an existing pipe object and wait if not yet existent
|
|
avixPipe_Read
|
Read data from a pipe and wait if not sufficient data available
|
|
avixPipe_Write
|
Write data to a pipe and wait if not enough empty space in the pipe to write
|
|
avixPipe_ReadFromISR
|
Read data from a pipe from an Interrupt Service Routine
|
|
avixPipe_WriteFromISR
|
Write data to a pipe from an interrupt service routine
|
|
avixPipe_StopDeviceFromISR
|
Stop a device from an interrupt service routine
|
|
|
|
|
|
|
Memory pool functions allow for creation of memory pools with fixed size memory blocks. Memory blocks can be allocated from and returned to the pool. While allocated, memory blocks can be used to hold user specified data for whatever purpose needed.
|
|
Function
|
Description
|
|
avixMemPool_Create
|
Create a new memory pool object
|
|
avixMemPool_Get
|
Get an id of an existing memory pool object and wait if not yet existent
|
|
avixMemPool_Allocate
|
Allocate a memory block from a memory pool
|
|
avixMemPool_Free
|
Return a memory block to its pool
|
|
avixMemPool_AllocateFromISR
|
Allocate a memory block from a pool from within an Interrupt Service Routine
|
|
avixMemPool_FreeFromISR
|
Return a memory block to its pool from within an Interrupt Service Routine
|
|
|
|
|
|