| .. _common_float: |
| |
| Floating Point Services |
| ####################### |
| |
| .. note:: |
| Floating point services are currently available only for platforms |
| based on the Intel x86 architecture. |
| |
| Concepts |
| ******** |
| |
| The kernel allows an application's tasks and fibers to use floating point |
| registers on board configurations that support these registers. |
| Threads that use the x87 FPU/MMX registers are known as "FPU users", |
| while threads that use SSE registers are known as "SSE users". |
| |
| .. note:: |
| The kernel does not support the use of floating point registers by ISRs. |
| |
| The kernel can be configured to provide only the floating point services |
| required by an application. Three modes of operation are supported, |
| which are described below. In addition, the kernel's support for the SSE |
| registers can be included or omitted, as desired. |
| |
| No FP registers mode |
| ==================== |
| |
| This mode is used when the application has no tasks or fibers that use |
| floating point registers. It is the kernel's default floating point services |
| mode. |
| |
| If a task or fiber uses any floating point register, |
| the kernel generates a fatal error condition and aborts the thread. |
| |
| Unshared FP registers mode |
| ========================== |
| |
| This mode is used when the application has only a single task or fiber |
| that uses floating point registers. |
| |
| The kernel initializes the floating point registers so they can be used |
| by any task or fiber. The floating point registers are left unchanged |
| whenever a context switch occurs. |
| |
| .. note:: |
| Incorrect operation may result if two or more tasks or fibers use |
| floating point registers, as the kernel does not attempt to detect |
| (or prevent) multiple threads from using these registers. |
| |
| Shared FP registers mode |
| ======================== |
| |
| This mode is used when the application has two or more tasks or fibers |
| that use floating point registers. |
| |
| The kernel initializes the floating point registers so they can be used |
| by any task or fiber, then saves and restores these registers during |
| context switches to ensure the computations performed by each FPU user |
| or SSE user are not impacted by the computations performed by the other users. |
| A "lazy save" algorithm is used during context switching which updates the |
| floating point registers only when it is absolutely necessary---for example, |
| the registers are *not* saved when switching from an FPU user to a thread |
| that does not use the floating point registers, and then switching back |
| to the original FPU user. |
| |
| Every task that uses the floating point registers must provide stack space |
| where the kernel can save the registers during context switches. An FPU user |
| must provide 108 bytes of added stack space, above and beyond its normal |
| requirements; an SSE user must provide 464 bytes of added stack space. |
| |
| .. note:: |
| A task that does *not* use the floating point registers does not need |
| to provide any added stack space. A fiber does *not* need to provide any |
| added stack space, regardless of whether or not it uses the floating |
| point registers. |
| |
| The kernel automatically detects that a given task or fiber is using |
| the floating point registers the first time the thread accesses them. |
| The thread is tagged as an SSE user if the kernel has been configured |
| to support the SSE registers, or as an FPU user if the SSE registers are |
| not supported. If this would result in a thread that is an FPU user being |
| tagged as an SSE user, or if the application wants to avoid the exception |
| handling overhead involved in auto-tagging threads, it is possible to |
| pre-tag a thread using one of the techniques listed below. |
| |
| * A task or fiber can tag itself as an FPU user or SSE user by calling |
| :c:func:`task_float_enable()` or :c:func:`fiber_float_enable()` |
| once it has started executing. |
| |
| * A fiber can be tagged as an FPU user or SSE user by its creator |
| when the fiber is started. |
| |
| * A microkernel task can be tagged as an FPU user or SSE user by adding it |
| to the :c:macro:`FPU` task group or the :c:macro:`SSE` task group |
| when the task is defined. |
| |
| .. note:: |
| Adding the task to the :c:macro:`FPU` or :c:macro:`SSE` task groups |
| by calling :c:func:`task_group_join()` does *not* tag the task |
| as an FPU user or SSE user. |
| |
| If a task or fiber uses the floating point registers infrequently |
| it can call :c:func:`task_float_disable()` or :c:func:`fiber_float_disable()` |
| to remove its tagging as an FPU user or SSE user. This eliminates the need |
| for the kernel to take steps to preserve the contents of the floating point |
| registers during context switches when there is no need to do so. |
| When the thread again needs to use the floating point registers it can re-tag |
| itself as an FPU user or SSE user using one of the techniques listed above. |
| |
| |
| Purpose |
| ******* |
| |
| Use the kernel floating point services when an application needs to |
| perform floating point operations. |
| |
| |
| Usage |
| ***** |
| |
| Configuring Floating Point Services |
| =================================== |
| |
| To configure unshared FP registers mode, enable the :option:`FLOAT` |
| configuration option and leave the :option:`FP_SHARING` configuration option |
| disabled. |
| |
| To configure shared FP registers mode, enable both the :option:`FLOAT` |
| configuration option and the :option:`FP_SHARING` configuration option. |
| Also, ensure that any task that uses the floating point registers has |
| sufficient added stack space for saving floating point register values |
| during context switches, as described above. |
| |
| Use the :option:`SSE` configuration option to enable support for |
| SSEx instructions. |
| |
| |
| Example: Performing Floating Point Arithmetic |
| ============================================= |
| This code shows how a routine can use floating point arithmetic to avoid |
| overflow issues when computing the average of a series of integer values. |
| Note that no special coding is required if the kernel is properly configured. |
| |
| .. code-block:: c |
| |
| int average(int *values, int num_values) |
| { |
| double sum; |
| int i; |
| |
| sum = 0.0; |
| |
| for (i = 0; i < num_values; i++) { |
| sum += *values; |
| values++; |
| } |
| |
| return (int)((sum / num_values) + 0.5); |
| } |
| |
| APIs |
| **** |
| |
| The following floating point services APIs are provided by |
| :file:`microkernel.h` and by :file:`nanokernel.h`: |
| |
| :c:func:`fiber_float_enable()` |
| Tells the kernel that the specified task or fiber is now an FPU user |
| or SSE user. |
| |
| :c:func:`task_float_enable()` |
| Tells the kernel that the specified task or fiber is now an FPU user |
| or SSE user. |
| |
| :c:func:`fiber_float_disable()` |
| Tells the kernel that the specified task or fiber is no longer an FPU user |
| or SSE user. |
| |
| :c:func:`task_float_disable()` |
| Tells the kernel that the specified task or fiber is no longer an FPU user |
| or SSE user. |