首页 / 资讯中心 / 文章详情

MIPI RAW与Unpacked RAW:链路格式、内存格式及调试实战

MIPI RAW与Unpacked RAW:链路格式、内存格式及调试实战 ★ FEATURED ARTICLE
调camera驱动这些年MIPI raw和unpacked raw这两个词反反复复出现几乎每一次sensor出图异常都能牵扯到它们。Sensor明明输出的RAW10ISP那边却收到了一堆错位的bit流画面全是斜条纹又或者DMA里配的是unpacked存储sensor手册里写的却是packed传输两边对不上光排查格式就耗掉一下午。这篇文章把这两个概念的来龙去脉一次说透链路格式和内存格式到底是什么关系packed和unpacked在带宽、对齐、处理效率上的差异有多少从硬件解包到软件转换怎么做最后给一套可以直接复用的排查方法适合驱动工程师、ISP开发者和做采集设备的人参考。1. MIPI raw和unpacked raw先分清链路格式与内存格式完全两回事很多人把MIPI raw和unpacked raw当成两种可以二选一的存储格式这是个误区。MIPI raw描述的是MIPI CSI-2链路上的传输格式unpacked raw描述的是数据落到内存后的布局。链路和内存中间还隔着一层DMA搬运这层可能做解包也可能不做所以两者不能直接画等号。1.1 MIPI链路上的RAW数据长什么样MIPI CSI-2是串行协议数据以包为单位在D-PHY或C-PHY物理链路上传输。每个包有包头、载荷和包尾包头里的数据类型字段Data Type简称DT负责告诉接收端载荷里装的到底是YUV422还是RAW Bayer位深是多少。RAW数据的常见位深包括RAW6、RAW7、RAW8、RAW10、RAW12、RAW14、RAW16。其中RAW8因为正好1字节一个像素天然对齐不存在打包问题。RAW10开始就麻烦了10bit不是字节的整数倍为了在物理链路上不浪费bit位标准做法是把多个像素的bit连续拼凑起来。RAW10的打包规则是4个像素拼成5字节。为什么是4个因为4乘10bit等于40bit正好5字节这是能整除的最小组合再往下3个像素30bit等于3.75字节没法按字节封包。RAW12则是2个像素拼3字节RAW14是4个像素拼7字节。这种把有效bit紧密排列的传输格式就是packed。打个比方这就像几个人合住一间房按床位分配空间利用率高但你要找某个人的东西就得在一堆行李里翻。链路侧还有一个容易踩的坑不同版本规范的DT编号不一样。比如内核驱动里常见的定义中RAW8对应0x2BRAW10对应0x2CRAW12对应0x2DRAW14对应0x2E。但老的sensor datasheet里可能写的是其他编号甚至有些厂商把RAW10 unpacked单独编了一个DT码。实际开发一律以sensor手册和控制器驱动里的枚举为准不要拿着记忆里的编号套所有平台。1.2 内存里的unpacked到底是什么当MIPI控制器把包解析完payload会交给DMA写进DRAM。这时候数据到底以什么方式排列取决于DMA控制器而不是MIPI链路本身。如果DMA直接原样搬运内存里保存的就是packed格式如果DMA在做搬运时顺带做了像素解包每个10bit像素被扩展成16bit内存里就是unpacked格式低10位是有效数据高6位一般补0。unpacked的存储特点可以概括为每个像素独立对齐到一个固定的字节容器里。10bit和12bit像素最常见的容器是2字节少数老平台会放到4字节里。这样做的直接好处是CPU、DSP、ISP在处理时可以一次load一个像素不需要做位移拼接。坏处也直观存储容量和带宽都会明显增加。说一个容易混淆的点有很多sensor直接输出unpacked格式走MIPI链路此时DT可能仍然是RAW10但packet payload里的bit不是紧密排列的而是每个像素占固定bit数或者字节数。这种场景在工业相机里很常见。所以拿到一帧数据不能只看DT判断内存排布链路格式和内存格式要分别确认这是我反复强调的核心。2. 一本账看清存储差异带宽、内存、对齐全量化理解了定义接下来用数字说话。不同位深的packed和unpacked在数据量上的差距有多大会影响系统设计时选链路条数、DDR带宽、buffer大小值得认真算一笔。2.1 不同位深下每像素占用量对比位深packed每像素字节数unpacked常见容器字节数1080p单帧packed数据量1080p单帧unpacked数据量unpacked相对packed增幅RAW8112,073,600 B2,073,600 B0%RAW101.2522,592,000 B4,147,200 B60%RAW121.523,110,400 B4,147,200 B33%RAW141.7523,628,800 B4,147,200 B14%以1920乘1080的RAW10为例packed一帧数据量是1920乘1080乘1.25等于2,592,000字节unpacked是1920乘1080乘2等于4,147,200字节。差距1,555,200字节换算成30fps实时流带宽从77.76MB/s直接涨到124.42MB/s。D-PHY链路上每lane的带宽本来就要精打细算这一下多出46.66MB/s很可能逼着你多开一条lane。所以我见过很多项目在选型阶段就把RAW10定成packed传输目的就是省MIPI lane。2.2 内存带宽与系统性能的连锁反应数据量增加不只是占存储空间更直接影响DDR带宽。四路摄像头同时采集的场景里一路RAW10从packed切到unpacked每帧多出1.5MB数据四路就是6MB30fps就是180MB/s额外写入量。DDR总带宽是共享的CPU跑算法、GPU做渲染、显示控制器扫屏都在抢这部分资源。之前某多路采集系统就是这样某一路改成unpacked后主核跑计算任务时周期性卡顿perf一查DDR write带宽被camera pipeline吃掉了将近一半只能把unpacked改回packed才解决。有人可能会说那就让DMA在传输时保持packed算法处理时再临时解包这样链路省带宽内存省空间。没错这是很多移动平台的默认做法。代价是需要额外的解包开销要么放在DMA硬件里做要么占用CPU。后续章节会讲这两条路怎么选。2.3 行宽与stride对齐最容易忽略的坑数据量不是唯一要关心的对齐规则才是debug噩梦的来源。MIPI payload按字节对齐RAW10一行数据在链路层按4像素一组展开行宽如果正好是4的倍数字节数是整数如果不是就会产生多余的bit。多数sensor通过内部裁切或忽略处理来避免这种尴尬但作为接收方你不能假设行宽一定整除。DMA写入内存时硬件对行起始地址有对齐要求常见是32字节或64字节。假设你开一个1200宽度的ROIRAW10 packed一行的有效字节是1200乘1.25等于1500字节但DMA要求32字节对齐stride会被拉大到1504字节末尾多4字节填充。如果读取端没有按stride跳行而是按有效字节数依次读第二行起就会错位4字节之后每行累加错位画面呈现典型的斜向横纹和sensor输出坏点非常像。这类问题最好的排查方法是打印每一行起始地址或者用工具查看buffer的线性排列确认stride跟有效宽度到底差多少。我在实际调试中这个坑遇到过不止三次每次都被当成sensor时序问题查半天其实只要把格式配置里的stride字段改成DMA要求的值就正常了。2.4 选型逻辑什么场景该用哪种不存在绝对正确的存储方式只看瓶颈在哪里。移动端SoC的摄像头pipelineMIPI lane数量和DDR带宽都紧张普遍采用packed传输、硬件解包后以unpacked存储到内存兼顾链路节省和后续处理速度。工业相机或医疗设备如果分辨率低、帧率要求不高CPU拿到数据只想赶紧算直接配置sensor输出unpacked链路上也不打包省掉一层转换。还有些系统内存非常充裕、MIPI lane数固定这时候unpacked链路的简洁性更值得因为不需要在CSI控制器里配像素转换寄存器软件复杂度更低。3. 从链路到内存的格式转换硬件解包与软件解包格式在链路和内存之间发生转换中间是有真实硬件在干活的。搞明白这条通路你才能知道问题到底出在哪一层。我习惯把它拆成三步sensor把RAW发出来CSI控制器收包解析DMA按配置写内存。任何一步理解错图像都会坏。3.1 一条完整的RAW数据通路Sensor输出的RAW数据经过MIPI D-PHY或C-PHY物理层进入CSI-2控制器。控制器做协议解析包括ECC校验、CRC校验、把串行bit恢复成字节再把payload交给下游。下游有两种常见架构一种是CSI和ISP内联ISP直接消费payload内存里只留ISP处理后的图像或者解包后的raw副本另一种是CSI后面只挂DMA数据原样写进DDR由软件决定如何解释。第二种架构下内存里的布局就完全由DMA配置决定。我调过的某平台CSI控制器支持一种raw dump模式专门用来把sensor原始数据存到内存给调试工具用。这个模式里有个像素格式选择字段写成RAW10 packedDMA就把5字节一组原样搬运写成RAW10 unpackedDMA内部做位扩展把4像素5字节展开成4像素8字节。第一次听说这个功能的人经常以为链路DT会跟着变其实DT还是同一个变的只是DMA的写内存策略。3.2 硬件自动解包的原理与配置硬件解包本质上是个位重排电路。以RAW10为例DMA收到5字节要把其中4个像素分别提取出来每个像素扩展成16bit高6位补0然后连续写8字节。这个过程完全可以在搬运的同时做不额外增加DDR读写次数代价只是芯片面积和功耗增加一点。所以很多移动芯片都内置这个功能驱动里往往有一个类似pixel_store_format的寄存器字段。配置这类寄存器时最容易翻车的是把链路的DT和store format搞混。DT是sensor层面描述payload含义的store format是CSI控制器描述自己怎么落地的。两个独立配置一错就出现链路明明对内存全乱的场面。我建议每调一个新平台先把这两项单独列出来跟sensor手册和SoC用户手册逐字核对一遍不要想当然。3.3 软件解包代码RAW10 packed转unpacked硬件不支持解包或者你想脱离平台在PC上处理裸数据时就需要软件解包。下面这段C代码是RAW10 packed转unpacked的常用实现前提是位序约定和标准MIPI规范一致即每个像素的高8bit在前、低2bit在后一组负载按5字节连续排布。static void raw10_packed_to_unpacked(const uint8_t *src, uint16_t *dst, int width) { int i 0; while (i 3 width) { uint8_t b0 src[0]; uint8_t b1 src[1]; uint8_t b2 src[2]; uint8_t b3 src[3]; uint8_t b4 src[4]; dst[0] (uint16_t)((b0 2) | (b1 6)); dst[1] (uint16_t)(((b1 0x3f) 4) | (b2 4)); dst[2] (uint16_t)(((b2 0x0f) 6) | (b3 2)); dst[3] (uint16_t)(((b3 0x03) 8) | b4); src 5; dst 4; i 4; } /* 剩余不足4个像素时按实际需求处理通常丢弃或补零 */ while (i width) { dst[i] 0; i; } }这段代码看起来简单但位运算的细节一个都不能错。b0是高8bit直接放到结果的高8位b1里高6bit属于第一个像素的低2位不对反过来b1的高2位是第一个像素的低2位其余6位是第二个像素的高6位。所以dst[0]等于b0左移2位加上b1右移6位这个右移拿到的正是b1的高2位。后面三个像素同理只是移动量逐级递减。如果你最终调试出来的图像颜色顺序不对先检查位序是LSB first还是MSB first再决定是否把所有位移方向反过来。3.4 反向转换unpacked回packed有时候你会遇到反过来的需求算法处理完了想把unpacked RAW重新打包传给下游。逆操作的逻辑正好是上面解包的倒放把4个16bit像素的低10位重新拼接成5字节。static void raw10_unpacked_to_packed(const uint16_t *src, uint8_t *dst, int width) { int i 0; while (i 3 width) { uint16_t p0 src[0] 0x03ff; uint16_t p1 src[1] 0x03ff; uint16_t p2 src[2] 0x03ff; uint16_t p3 src[3] 0x03ff; dst[0] (uint8_t)(p0 2); dst[1] (uint8_t)(((p0 0x03) 6) | (p1 4)); dst[2] (uint8_t)(((p1 0x0f) 4) | (p2 6)); dst[3] (uint8_t)(((p2 0x3f) 2) | (p3 8)); dst[4] (uint8_t)(p3 0xff); src 4; dst 5; i 4; } }和packed转unpacked一样这个函数对位序极其敏感。如果你是在某个具体平台上跑强烈建议先用一张已知像素值的测试图验证我一般会先构造全0、全1023、0到1023渐变三张图跑一遍输出和预期一致才放心大批量处理。3.5 Linux V4L2里如何区分packed与unpacked在Linux下做camera驱动格式命名更直接。链路侧用media bus format比如10bit Bayer有SRGGB10_1X10和SRGGB10_2X8_PADHI_LE两种前者是packed后者是unpacked小端且高位补0。内存侧用V4L2 pixel formatSRGGB10P是packedSRGGB10是unpacked。两套命名对应两个不同配置点配置时必须保证上下游一致。我调过的某平台sensor驱动sensor侧上报的bus format写成SRGGB10_2X8_PADHI_LE也就是unpacked链路但CSI DMA侧配置成了输出packed到内存。结果图像看着像蒙了层噪声每个像素的高6bit全是随机垃圾。当时还怀疑sensor寄存器配错后来用喂值法把2乘2像素的已知图像发进去才发现总线格式和内存格式根本没对应上。这件事之后我在所有camera驱动代码里都养成了一个习惯链路格式和内存格式分开打印注册时严格校验匹配关系不匹配直接报错。4. 实操把packed raw切到unpacked的四个关键步骤如果你接到一个任务要让某颗sensor从默认的packed RAW10改成unpacked输出或者反过来让CSI DMA从packed解包改成原样搬运下面这套流程基本通用。每一步都有检查点防止把问题拖到下一个环节。4.1 第一步确认sensor输出模式与DT编号先在sensor datasheet里找RAW格式输出寄存器确认它支持的输出模式。有的sensor只支持packed有的支持两种靠寄存器bit切换。同时把MIPI packet里带出的DT编号记下来。判断标准很简单如果sensor手册里写明MIPI DT是RAW10且没有提供unpacked寄存器开关那无论内存里想用什么格式链路侧都是packed剩下的事情交给DMA去做解包。还有一种容易搞混的情况sensor手册里写RAW10 unpacked每像素2字节这时DT仍然可能是RAW10但packet payload不再是紧密排列每个像素占2字节或者按2字节对齐。这种模式对MIPI lane带宽极不友好所以很少在手机camera里用。遇到这种描述你反而要确认CSI控制器能不能识别这种payload有些老控制器不支持会按照packed去读整帧画面必花。4.2 第二步配置DMA像素存储格式确认sensor输出后打开SoC的CSI控制器驱动找到像素存储格式配置项。通常会有packed、unpacked、半打包之类的选项。这里的单词可能叫raw_store_mode、pixel_width、fmt_store等等不同平台命名差异很大但概念一致。你要想清楚到底想从这一步开始让内存变成什么布局而不是简单照搬sensor侧的格式。配置完成后抓一帧raw用十六进制编辑器直接看buffer头几个字节。如果sensor输出的是纯灰阶图理论上所有像素值应该接近同一个数。如果看到每5字节里出现了明显的规律性空位比如每个10bit像素后面跟着6bit零说明DMA把unpacked写出来了而sensor给的还是packed中间一定有某个转换被跳过。反过来也一样内存看起来紧密排列但预期是unpacked就能定位到DMA没有做解包。4.3 第三步用工具链验证media bus和V4L2格式在Linux环境里我习惯用media-ctl直接看sensor subdev输出的当前bus format再用v4l2-ctl查capture节点的像素格式。两条命令的输出会被很多新手忽略但它们一对比就能看出链路和内存两个层面有没有对齐。如果sensor那边显示SRGGB10_1X10capture这边却是V4L2_PIX_FMT_SRGGB10那中间必然存在某个转换层要么是DMA解包要么是CSI驱动内做了一次格式翻译。你确认这个转换逻辑确实存在且配置正确才能继续往下走。有些驱动会在set_format回调里自动把sensor的bus format翻译成对应的V4L2 pixel format翻译表做得不完整时会出现一个能设一个报错的情况。遇到这种格式链验证就不是简单的命令查看而是要翻驱动的格式映射表看看有没有遗漏的位深组合。4.4 第四步抓帧验证字节序和像素值格式链没问题不代表bit顺序一定对。强烈建议用黑白半图卡或者纯色画面做最终验证。抓回raw后先看左上角第一个像素值是否接近预期再看整幅图像是否还有左右颠倒、上下颠倒、颜色通道错乱的现象。字节序反了的典型表现是图像看起来有错位但raw buffer里每个像素的数值转移位置后完全正常。我之前调试一颗500万sensor时默认配置出来整幅画面每两列就有一条黑线怎么看都像sensor行拼接问题。后来把抓到的raw按每像素2字节展开发现每两个有效像素中间夹着一个固定值0x00C0这根本不是图像数据而是DMA把unpacked当成packed输出后残留的填充。改完DMA的存储格式配置一条黑线都没有了。备查的小技巧只看像素均值是看不出问题的把某一行数据打印出来当文本看任何规律性插入都是配置错误的线索。5. 花屏、错位、噪点常见问题与排查实录做camera调试格式问题导致的故障表现有规律可循。看到特定现象先往特定方向查能省很多时间。下面把常见问题按现象归类附上排查优先级。5.1 现象一斜向条纹整个画面像歪了这种通常不是sensor坏而是接收端按错误的stride读数据。每一行实际比预期宽了几个字节导致下一行起始位置偏移越往下偏移越多形成斜纹。排查顺序先确认DMA的stride配置和图像宽度算出来的字节数是否一致比如你是不是用1200像素去读而DMA配的是1500字节的stride两者不匹配。其次确认sensor输出的行有效像素是否包含水平消隐很多sensor datasheet里有效宽度并不等于整行长度驱动要用包含消隐的总长度去算stride。5.2 现象二整幅图像像蒙了一层雪花噪声每个像素的高位或低位出现随机值这种大多是packed/unpacked解包错位。链路传输的是packed但内存按unpacked读或者DMA解包做的位扩展方向和实际bit序相反。先打印一行raw数据数一下每几个字节构成一个像素。如果发现每5字节对应4像素的规律基本就是packed格式每2字节对应1像素就是unpacked。然后看这2字节里高6位是不是全0如果不为0且没有规律说明解包位序错了或补位方向反了。5.3 现象三左侧边缘或者每行开头出现半像素错位这种多在RAW10或者RAW12下出现。MIPI包里的行payload可能不是从sensor第一个有效像素开始而是包含几个用于对齐的dummy像素过去我们在解析时直接忽略了。但DMA搬运时不会自动跳过会把dummy也当作有效数据写进内存于是每行开头就多了几个像素图像看起来像每行左移了一点。解决方法是在驱动的行配置里设置像素偏移量或者把sensor的水平尺寸配大再在内存侧做裁剪。5.4 快速确认packed/unpacked的土办法不用示波器也不用高端仿真器打印第一行前20个像素的十六进制值就能判断当前内存布局。假设画面是均匀灰卡RAW10有效值理论上应该集中在某个小区间。如果前20个值都是0xFF、0xFC这种高8位连续的数且每5字节一组的字节数严格等于4像素那是packed。如果每2字节一组、低10位有效、高6位全0那是unpacked。如果完全看不出规律先检查DMA是否被错误配置再检查sensor是否真的在输出RAW而不是YUV。5.5 避坑速查表检查项正确做法常见错误链路DT以sensor手册和驱动枚举为准凭记忆认定某个编号行stride用DMA对齐要求后的值直接用有效像素字节数字节序用已知图像验证LSB/MSB方向默认一定是大端补位方向unpacked高位补0还是低位补0要确认不分PADHI/PADLOmedia bus与V4L2格式链路、内存分别确认并匹配只配一边水平消隐像素总数包含消隐只算可见像素个人经验里大部分RAW格式问题最终都能归结为某层配置和实际数据不对应。不要一上来怀疑sensor先花10分钟验证内存帧的字节规律经常比翻半天datasheet更高效。最后再分享一个我目前所有camera调试都会做的动作拿到一颗新sensor或新平台先抓一帧已知灰阶图按上面说的方式把整个buffer的字节规律看一遍再决定要不要继续调别的寄存器。这比直接对着文档猜格式靠谱得多。格式链理顺了后面算法处理、ISP调参都会顺畅一大截。
阅读完成 · 觉得有帮助?
咨询建站