浮点服务

Note:浮点服务目前仅对基于Intel X86架构的平台可用。

概念

Zephyr的内核允许应用的任务或线在支持浮点寄存器的板卡配置上使用浮点寄存器。使用X87 FPU/MMX寄存器的线程被称为“FPU用户”,使用SSE寄存器的线程被称为"SSE用户"。

Note:内核不支持在ISR中使用浮点寄存器。

内核能够被配置为仅为需要浮点服务的应用提供浮点服务。下面介绍了三种支持的操作模式。此外,内核对SSE寄存器的支持能够根据需要被包含或被省略。

无FP寄存器模式

当应用不包含使用浮点寄存器的任务或线时采用这种模式。该模式是内核默认的浮点服务模式。

如果任务或线使用了任何的浮点寄存器,内核会产生致命错误状态并且终止该线程。

非共享FP寄存器模式

当应用仅包含一个需要使用浮点寄存器的任务或线的时候使用这种模式。

内核初始化浮点寄存器使得他们能够被任何的任务或线使用。无论发生任何种类的上下文切换,浮点寄存器内容保持不变。

Note:如果两个或多个任务或线使用浮点寄存器时,可能会导致不正确的结果。内核不会尝试检测(或阻止)多个线程对这些浮点寄存器的使用。

共享FP寄存器模式

当应用包含有两个或两个以上的任务或线需要使用浮点寄存器时采用该模式。

内核初始化浮点寄存器使得这些寄存器能够被任何任务或线使用,在上线文切换时,保存和恢复这些寄存器来保证每个FPU用户或SSE用户执行的运算不会被其他用户执行的运算所影响。在上下文切换时,“lazy save”算法的采用让寄存器的更新推迟至他们必须得到更新时。例如,当从一个FPU用户切换至其他的不使用浮点寄存器的线程,再切换回来的时候,浮点寄存器并不会被保存。

每个使用浮点寄存器的任务必须提供栈空间用来让内核在发生上下文切换时保存寄存器。FPU用户必须提供108字节的额外栈空间,超出其正常的要求;SSE用户必须提供额外的464字节栈空间。

Note:不使用浮点寄存器的任务并不需要提供额外的栈空间。线不需要提供额外的栈空间,无论他们是否使用浮点寄存器。

当给定的任务或线第一次访问浮点寄存器时,内核能够自动地探测给他们是否使用了浮点寄存器。这些线程会被标记为SSE用户当内核被配置为支持SSE寄存器时,或者被标记位FPU用户当SSE寄存器不被支持的情况下。如果某个FPU用户线程被标记为SSE用户,或者如果应用想要避免包含在自动标记线程中的异常处理开销,那么应该使用下面列举的预标记线程技术。

  • 任务或线能够通过在其一次开始执行的时候调用task_float_enable()fiber_float_enable()函数来标记自己为FPU用户或SSE用户。
  • 在线开始的时候,能够被创建者标记为FPU用户或者SSE用户。
  • 在微内核中,任务能够在被定义时通过将其添加进FPU任务组或SSE任务组来标记自己为FPU用户或者SSE用户。

Note:通过调用task_group_join()函数来添加任务到FPUSSE任务组并不会标记该任务为FPU用户或SSE用户。

如果任务或线对浮点寄存器的使用并不频繁,能够通过调用task_float_disable()fiber_float_disable()函数来移除自身的FPU用户或SSE用户标记。这样在内核进行上线文切换时就消除了本来就无用的保存浮点寄存器内容的步骤。当线程再次需要使用浮点寄存器时,它能够通过使用上面提到的方法,重新标记自身位FPU用户或SSE用户。

目的

当应用需要执行浮点数操作时采用内核浮点服务。

使用方法

配置浮点服务

配置非共享FP寄存器模式,需要开启FLOAT配置选项并禁用FP_SHARING配置选项。

配置共享FP寄存器模式,需同时开启FLOAT配置选项和FP_SHARING配置选项。此外,还需要保证任何使用浮点寄存器的任务包含有足够的额外栈空间在进行上下文切换时用来保存浮点寄存器的值。

使用SSE配置选项来开启对SSEx指令的支持。

示例:执行浮点数运算

下面的代码说明了函数如果使用浮点运算来避免在对一组整数进行平均值计算时的溢出问题。需要注意的时,如果内核配置正确的话,并不需要特殊的额外代码。

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

API

下面的浮点数服务API由microkernel.hnanokernel.h头文件导出。

fiber_float_enable()

通知内核某个指明的任务或线现在是一个FPU用户或SSE用户。

task_float_enable()

通知内核某个指明的任务或线现在是一个FPU用户或SSE用户。

fiber_float_disable()

通知内核某个指明的任务或线现在不再是一个FPU用户或SSE用户。

task_float_disable()

通知内核某个指明的任务或线现在不再是一个FPU用户或SSE用户。