浮点服务

内核允许支持浮点寄存器的板子使用能够浮点寄存器。

注解

浮点服务当前仅对基于 ARM Cortex-M4 和 因特尔 x86 架构的板子有效。该服务是架构相关的。内核不支持在 ISR 中使用浮点服务。

概念

可以配置内核让其支持浮点服务。一共支持三种操作,详见下面的描述。此外,内核还支持包括/忽略 SSE 寄存器。

无浮点寄存器模式

当没有线程使用浮点寄存器时使用这种模式。这是内核的默认浮点服务模式。

如果某个线程使用了任何浮点寄存器,内核将产生一个致命错误,并终止该线程。

非共享浮点寄存器模式

当应用程序只有一个线程使用了浮点寄存器时使用这种模式。

内核会初始化浮点寄存器,然后任何线程都可以使用这些寄存器。当发生上下文切换时,浮点寄存器不会改变。

注解

如果两个或多个线程使用了浮点寄存器,则有可能导致不正确的结果,因为内核不会去检测(或阻止)多线程去使用这些寄存器。

共享浮点寄存器模式

当应用程序有两个或多个线程需要使用浮点寄存器时使用这种模式。依赖于可能的 CPU 架构,内核支持如果一个或多个线程子类:

  • 非用户:不能使用任何浮点寄存器的线程。
  • FPU 用户:可以使用标准浮点寄存器的线程。
  • SSE 用户:可以使用标志浮点寄存器和 SSE 寄存器的线程。

内核会初始化浮点寄存器,然后任何线程都可以使用该寄存器。当发生上下文切换时,内核会保存并恢复这些寄存器,以确保每个 FPU 用户或者 SSE 用户在执行浮点运算时不会影响其它用户。

对于 ARM Cortex-M4 架构,当浮点寄存器被使能时,所有的 线程都被认为是 FPU 用户。也就是说,即使相关线程没有使用浮点寄存器,在进行上下文切换的时候也会保存或恢复这些寄存器。每个线程必须提供额外的 132 字节的栈空间,用于存放这些寄存器的值。

对i与 x86 架构,内核会正确地区分非用户、FPU 用户和 SSE 用户。只有当绝对需要时,内核才会在上下文切换期间使用一个叫做“懒惰存储”的算法更新浮点寄存器的值。例如,当从 FPU 用户切换到非用户时,以及再切换回去时,内核都不会更新浮点寄存器。下标列举了各种情况下所需要的额外的栈空间。

线程类型 浮点寄存器 需要使用额外的栈空间
协作式 any 0 字节
抢占式 0 字节
抢占式 FPU 108 字节
抢占式 SSE 464 字节

在 x86 架构下,当线程第一次访问浮点寄存器时,内核会自动检测到,然后该线程被标记为 SSE 用户(如果内核被配置支持 SSE 寄存器)或者 PFU 用户(如果内核被配置支持不支持 SSE 寄存器)。如果这可能导致一个 FUP 用户线程被标记为 SSE 用户,或者如果应用程序想避免被自动贴上标签的用户调用引起的异常处理开销,可以使用下面的技术给线程预先贴上标签:

  • 静态创建的 x86 线程通过传递 K_FP_REGS 或者 K_SSE_REGS 选项给 K_THREAD_DEFINE
  • 动态创建的 x86 线程通过传递 K_FP_REGS 或者 K_SSE_REGS 选项给 k_thread_spawn()
  • 对于已经创建的线程,当它在启动线程时传递 K_FP_REGSK_SSE_REGS 选项给 k_float_enable()

如果 x86 线程使用浮点寄存器的频率很低,它可以调用 k_float_disable() 来移除自己的 FPU 或者 SSE 用户标签。这样能在上下文切换时减小不必要的浮点寄存器服务。当线程再次需要使用浮点寄存器时,它可以调用 k_float_enable() 将其再次标记未 FPU 或 SSE 用户。

实现

执行浮点计算

如果内核相关选项配置好了,线程无须其它代码就能执行浮点运算。

下面的代码演示了如何利用浮点服务在计算整数的平均值时避免溢出的问题。

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);
}

建议的用法

当应用程序需要执行浮点操作时,使用内核的浮点服务。

配置选项

要配置非共享浮点寄存器模式,需要使能配置选项 CONFIG_FLOAT 并禁能配置选项 CONFIG_FP_SHARING

要配置共享浮点寄存器模式,需要使能配置选项 CONFIG_FLOATCONFIG_FP_SHARING 。另外,正如前面所述,要使用浮点寄存器的线程必须具有足够的栈空间。

配置选项 CONFIG_SSE 用于使能对 SSEx 指令(仅限于 x86)的支持。

API

头文件 kernel.h 中提供了如下的关于浮点的 API(只适用于 x86): The following floating point APIs (x86 only) are provided by kernel.h:

  • k_float_enable()
  • k_float_disable()