LUT编程
LUT是NVDLA中SDP/CDP的实例,用于模拟神经网络中的非线性函数(Sigmoid/TanH/LRN等。)。我们知道,LUT精度高度依赖于LUT entry和曲线的斜率变化:LUT entry越多,精度越高。另一方面,曲线的斜率变化越大,越难模拟。
值得一提的是,SDP/CDP共享相同的LUT逻辑,它们之间的唯一区别是位深度,因为SDP管道是32位,而CDP管道是37位。
这里提出了一种创新的2级混合LUT架构,通过有限的LUT entry来保持非常高的精度:
该实施方案有两个亮点:
LUT中有两个表(X表\Y表)和两种模式(X表有两种工作模式),典型配置是将其中一个用作原始表以覆盖整个动态范围,另一个用作密度表以覆盖一小部分动态范围。由于覆盖范围的差异,原始表具有较低的采样率,而密度表具有相对较高的采样率,这是受LRN/Sigmoid/TanH曲线属性的启发
从上面的图我们可以看出,对于这些函数,只有一小部分具有显著的斜率变化,而其他部分几乎没有太大的变化,因此2级LUT是模拟这些函数的经济选项。
由于密度表和原始表之间可能有重叠,我们有一个可编程的寄存器:“优先级”(pri),当一个样本适合两个表时,允许软件控制哪个LUT表输出应该作为最终输出。当然,建议是一直使用密度输出。
混合工作模式
我们注意到,对于LRN,输入动态范围非常高(0~10^8),但大多数样本都在一个小数据范围内:
上面的直方图是从GoogleNet的“pool1/norm1”层收集的,我们通过不同的x轴坐标系(线性和指数)查看了相同的数据。
我们可以看到,线性(均匀采样)视图将50%以上的样本合并到直方图中的一个点,而指数视图(不均匀采样)将这些样本区分到不同的直方图点,从而提供更好的分辨率。同样的策略可以用于LUT:如果LUT在指数模式下工作,我们对低范围值有很高的采样率,对高范围值有很低的采样率(这是公平的,因为它们在直方图中的频率很低)。这就是“指数”模式的思想。目前,只有X表能够在指数模式下工作,当该模式被启用时,覆盖范围被固定为 。
下表总结了NVDLA的LUT属性(当映射到硬件时,X/Y用于表示不同的表,X对应于LE(Linear/Exponent,线性/指数),而Y对应于LO(Linear Only, 仅线性)):
典型使用场景的推荐LUT配置:
然而,这不是强制性的,软件可以将LUT编程为以下任何情况(X/Y可以颠倒,这意味着总共6种情况)运行:
如第一张图所示,LUT有几个参数,让我们讨论如何根据不同的模式来配置它们。
指数工作模式
如果LUT正在指数模式下工作,LUT存储有如下示例(实际上,exp_start是可编程的):
假定对于每一个LUT存储,LUT的输入是,那么索引应该是
即
这需要乘法器,为了使硬件更简单,我们要求:
然后,硬件只需要右/左移位器来获得正确的索引,然而,也就是:
这可以通过LUT之前的变换器来保证(cdp中的cdp _ in _ cvt;SDP中的X/X/Y乘数)。 上述符号与实际寄存器之间的映射是(X可以是LE/LO):
溢出范围控制
假设一个LUT覆盖范围在[min,max]之间,如果一个输入样本大于max或小于min,我们称之为超范围样本。 NVDLA支持对这些超范围样本进行线性插值。插值的数学公式为(x是输入样本值):
从硬件的角度来说,插值是
以下溢为例,给定(是应用于LUT输入的缩放,SF是应用于LUT entry的缩放):
硬件输出为:
进而
上述公式中的符号与寄存器之间的映射为:
LUT存储编程
通常来说,为了对LUT entry进行编程,必须指定LUT entry地址及其值,这需要2次寄存器写操作。NVDLA通过引入硬件自动地址递增机制简化了这一过程,这意味着,当你需要对LUT表进行编程时,你只需编写如下代码(以LE表程序为例):
/* program raw table */
reg = (FIELD_ENUM(S_LUT_ACCESS_CFG, LUT_TABLE_ID, LE) << SHIFT(S_LUT_ACCESS_CFG, LUT_TABLE_ID)) |
(FIELD_ENUM(S_LUT_ACCESS_CFG, LUT_ACCESS_TYPE, WRITE) << SHIFT(S_LUT_ACCESS_CFG, LUT_ACCESS_TYPE));
reg_write(S_LUT_ACCESS_CFG, reg);
for(i = 0; i < LUT_LE_TABLE_ENTRIES; i++) {
reg_write(S_LUT_ACCESS_DATA, lut->le_table[i]);
}
如果地址超出了总的LUT entry(例如:上述伪代码中的LUT_RAW_TABLE_ENTRIES超出了实际的LUT entry),则硬件行为是未定义的。
NVDLA支持从任意入口读回已编程的LUT entry。LUT访问CFG只需要编程一次,地址就会自动增加。请注意,对于LUT读取的情况,S_LUT_ACCESS_CFG的编程必须是非后写的;
LUT编程有两个限制:
- 确保总是从第一个entry开始写LUT,并更新整个表;
- 对于两个寄存器组只有一个共享的LUT存储,确保当相应的子单元空闲时更新LUT;
命中/未命中行为
对于给定的输入样本,如果只命中一个表,最终输出将是命中表的输出;然而,X/Y表编程非常灵活,因此会导致不同的命中/未命中情况:
- 一个输入样本可能在两个表中都被命中;(case1)
- 由于溢出,一个输入样本可能在两个表中都丢失;(case1、2、3)
- 由于下溢,一个输入样本可能在两个表中都丢失;(case1、2、3)
- 由于一个表溢出而另一个表下溢,一个输入样本可能在两个表中都丢失(case3)
对于上述所有情况,硬件需要一种方法来选择如何获得最终输出,因此我们在下面公开了可编程寄存器,以允许软件对优先级进行编程:
LUT统计
当一个硬件层完成时,硬件将在下面报告统计数据,以帮助软件了解LUT表是否被合理编程。
对于每个寄存器组,我们在上面有专用的统计寄存器,当一个硬件完成时,这些计数器将可供读取(通过将生产者指针设置到该寄存器组)。在启用相应的寄存器组(op_en被置位)之前,这些统计数据不会被擦除。
end