本设计基于http://120.55.75.228:23080/zhdyz/hutp/-/tree/master/Design中文档 《HUTP.pdf》基础上进行设计。
采样率:子卡进行采集的采样率。可调,通常设定为一定范围内的整数值,如:80 000 000(80M),2 000 000(2M),500 000(500k),50 000(50k)等。目前约定,不同子卡的采样率,必须互为整数倍。
采样包:针对不同子卡采样率不一致的情况下,基于旧版的采集数据组织结构采样点,新设计的相关采集数据组织结构。
Blob容量:一个Blob包含的采样包数量。
子卡采样点:一张子卡单次采集下,所有通道数据本次采集的数据集合。
总采样数:一次采集的采样包数,当采够总采样数时,采样会停止。
通道掩码:表示各个子卡内相关AI/AO通道中,哪些通道采集数据会被实时传输。
降采样系数:表示各个子卡的子卡采样点中,需要从多少次采集的子卡采样点中抽取一个用于实时传输。
事务编号:用于表示采集工作的编号。
采集模式:载板采集数据工作形式,分为单次采集模式(SHOT MODE)、连续采集模式(STREAM MODE)。SHOT MODE,本次采集工作,采集数据达到总裁样数个采样点后会自行停止;STREAM MODE,本次采集工作,载板持续进行采集,直到收到停止采集的的外部触发信号或指令才会主动停止。
在之前版本的设计中,采集数据组织结构为采样点。采样点包含了单次采样里面,所有子卡的所有开启通道在本次采样中的全部采集数据。该设计里面,预设所有子卡按照统一采样率同步进行工作,因此,所有子卡同步进行采样。且在配置参数不变的情况下,采样点大小恒定。
例如,配置5张2M32ch AD子卡,通道全开,所有子卡按照2M采样率进行采集。此时,单个采样点包含5张子卡,每张子卡32通道的总计160通道采集数据,所有数据都是各个子卡同一时刻进行采样得到的。
当前版本里面,不同的AI采集卡,采样率可能存在不同,因此,某张子卡进行采样的同一时刻,其他子卡可能不会进行采样。此外,相同时间里面,不同的采集卡进行的采样次数也不相同。因此,对采样点概念进行相关拓展,设计采样包结构。
采样包:所有参与本次采集的采集卡数据源,在同一个时间窗口内产生的数据,按约定顺序组织形成的逻辑数据单元。每个数据源贡献该窗口内的一个或多个完整子卡采样点;各采集卡数据源提供的子卡采样点数量,由自身采样率和窗口长度决定。
当前约定,采样包表示的时间窗口,为最低采样率的对应采集卡的采样周期。由于我们已约定,不同子卡的采样率,必须互为整数倍,因此,该时间窗口长度必然为其他子卡的采样周期整数倍,在该时间窗口内,均可以完成整数次完整采样,次数为该子卡采样率 / 最低采样率。
例如,配置子卡0,1的采样率为2M,子卡2的采样率为40M,子卡3,4的采样率为80M,则采样窗口长度约定为500ns,在该窗口内,子卡0,1可以完整完成1次采样,子卡2可以完整完成20次采样,子卡3,4可以完成40次采样。
一个采样包时间窗口长度,对应最低采样率子卡的一个采样周期。在该时间窗口内,以最高采样率的采样周期划分若干个基本时隙,按照时间先后依次组织数据。
每个时隙内,仅写入在该时隙对应时刻进行采样的子卡数据;多张子卡在同一时刻采样时,按照子卡编号从小到大排列。每张子卡的一次采样数据,由该次采样的全部有效通道值按约定通道顺序组成。
未在当前时隙采样的子卡不写入数据,也不插入占位或补零字节。各时隙的数据连续紧密拼接,完成整个时间窗口后,即形成一个采样包。
使用该示例说明:
子卡0: 2M,32通道
子卡1: 2M,32通道
子卡2:40M, 4通道
子卡3:80M, 3通道
子卡4:80M, 3通道
此时,采样包的采样周期长度500ns,基本时隙为12.5ns,一个采样包包含40个基本时隙的采集数据。组包格式如下图所示:
在一个采样包里面:
| 时隙编号 | 相对时间 | 本时隙的数据排列 |
|---|---|---|
| 0 | 0ns | 子卡0第0点 → 子卡1第0点 → 子卡2第0点 → 子卡3第0点 → 子卡4第0点 |
| 1 | 12.5ns | 子卡3第1点 → 子卡4第1点 |
| 2 | 25ns | 子卡2第1点 → 子卡3第2点 → 子卡4第2点 |
| 3 | 37.5ns | 子卡3第3点 → 子卡4第3点 |
| 4 | 50ns | 子卡2第2点 → 子卡3第4点 → 子卡4第4点 |
| … | … | … |
| 38 | 475ns | 子卡2第19点 → 子卡3第38点 → 子卡4第38点 |
| 39 | 487.5ns | 子卡3第39点 → 子卡4第39点 |
其中,每个“子卡第几点”都展开成该子卡对应的一整组通道值。例如时隙0为:
sub0_point0_ch0 … sub0_point0_ch31 ->
sub1_point0_ch0 … sub1_point0_ch31 ->
sub2_point0_ch0 … sub2_point0_ch3 ->
sub3_point0_ch0 … sub3_point0_ch2 ->
sub4_point0_ch0 … sub4_point0_ch2
这里的时隙描述的是采样时间关系,不要求FPGA恰好每12.5ns完成一轮读FIFO;实际传输可以经过缓存后连续执行,只要保持上述排列顺序。64位传输拍也可以跨越通道组和时隙边界,不需要为每个时隙单独对齐补零。
当各个子卡采样率一致的时候,一个采样包时间窗口长度,等同于采样包内的基本时隙长度,此时,一个采样包只会包含一个采样时隙,每张子卡贡献的子卡采样点有且仅有一个。当各个子卡采样时刻对齐的时候,采样包组织结构和旧版的采样点结构一致。
因此,可以认为,旧版的采样点结构,属于现版的采样包结构在特殊的参数配置下的一种情况。
采样包不只是应用于实时数据传输,还会在数据流盘的文件组织、预处理模块中应用到相关结构。
另外,在实时数据传输过程中,还会有降采样操作,不同子卡降采样倍率可能不相同。此时,最终某通道的数据在实时传输里面,表现的实际采样率,既会受到子卡本身采集工作的采样率影响,也会受到载板端的对应子卡采集数据降采样倍率影响。最终载板实时采集数据传输过程中,进行采样包组织的时候,需要以经过降采样的实际采样率为各个子卡采样率的参考进行组包。
采样块(Blob):在采集过程中,传输的采集数据基本单元,由若干个采样包组成。
采样片(Slice):当Blob容量过大,单个UDP包无法完整传输一整个Blob,则需要将一个Blob,结合UDP包最大容量和各个采样包大小,将Blob分片为能够通过单个UDP包传输的多个采样片。
当一个采样包小于UDP包最大容量时,一个Slice对应着一个实际的UDP包,内部包含着多个采样包,一个采样包包含所有开启的子卡一个采样周期内采集到的数据。
采样包容量小于UDP包最大容量的情况下,HUTP传输单元格式示意图如下所示:
分片逻辑如下:
通常可设置UDP包payload的最大长度为2016字节(需开启巨型帧)(普通帧的最大MTU为1500字节,最大UDP payload为1500-20-8=1472字节)。
目前设定payload部分的头16个字节为UDP包的包头,后面的所有字节由多个采样包组成。
但是,在当前更新采样点结构为采样包结构后,一个采样包大小可能会大于一个UDP包最大容量。
例如如下极端情况:
子卡0,1的采样率为200k,每张子卡开启32通道;子卡2,3,4采样率为80M,每张子卡开启3通道。此时,按照前面所说的采样包组织格式,单个采样包内,会包含1个子卡0,1的子卡采样点数据,以及(80M/200k)=400个子卡2,3,4的子卡采样点数据。按照每通道2字节位宽计算,可得:此时一个采样包大小为2 * 1 * 32 * 2 + 3 * 400 * 3 * 2 = 7328字节,远大于前面所述的2016字节。
当不同子卡之间,采样率差距越大,则采样包大小越大,且没有上限。因此,建议对传输过程中的不同子卡采样率差距进行相关限制,避免采样包大小严重膨胀。目前建议不同子卡之间采样率差距不应大于100倍。另外,该情况下,约定分片内部,不跨采样包存储采集数据。
在这种情况下,需要对每个采样包进行分片处理。分片逻辑设计如下:
此时HUTP传输单元格式示意图如下所示:
另外,需要注意。在旧有采样点组织格式里面,单个采样点大小永远小于UDP包的最大容量,因此,不论是否存在丢包,上位机端永远可以接受到完整的采样点的采集数据。但是,现在的采样包组织格式里面,如果采样包大小大于单个UDP包最大容量,则丢包后,单个采样包内数据会出现缺失,直接严重影响上位机端的采集数据解析逻辑。因此,需要提供配套的丢包处理逻辑,保证采样包完整后再发布。
初步设计,在包头添加以下信息:
上位机端接收程序应当持续收包,同时维护重组缓存。
接收UDP数据
↓
检查包头、长度、偏移及配置
↓
放入对应采样包的重组缓存,更新已接收范围
├── 已完整:按顺序发布至共享内存
└── 未完整:保留缓存,继续接收其他包
周期性检查重组缓存
↓
超时仍不完整:丢弃该采样包,记录缺失序号,释放缓存
超时应从首次收到该采样包数据开始计时,且不因重复分片无限延后。超时时间应覆盖正常分片发送间隔和接收调度波动;完整包一旦收齐就立即处理,不需要等待超时到期。同时设置缓存数量和总容量上限,避免持续丢包时缓存无限增长。完整性检查必须确认全部字节范围都已覆盖且没有缺口。不能只把收到的长度相加,否则重复收到同一分片,可能被误判为已经收齐。
本文章使用limfx的vscode插件快速发布