「CIC→DMA→FIFO」的完整数据流程

一、基本定义

1. CIC模块:通信接口控制器(Communication Interface Controller)

CIC是硬件级的通信接口控制模块,常见于MCU/SoC/专用通信芯片中,核心作用是:

  • 封装底层通信协议(如SPI/I2C/UART/以太网/自定义工业总线);
  • 管理"上行/下行"数据的收发逻辑(上行:从外设→主机,下行:主机→外设);
  • 对接芯片内部的DMA控制器或CPU总线,负责数据的"桥接转发";
  • 典型场景:工业控制中传感器数据采集、ATE设备的工位数据上报、车载ECU的CAN数据转发等。
2. DMA:直接内存访问(Direct Memory Access)

DMA是脱离CPU独立完成数据传输的硬件模块,核心作用是:

  • 替代CPU搬运数据(如CIC的上行数据→内存/FIFO),解放CPU去处理逻辑运算;
  • 支持"批量数据传输"(按字节/按包/按中断触发),可配置传输阈值、地址、长度;
  • 关键特性:传输完成后会触发中断,通知CPU处理数据。
3. FIFO:先入先出缓存(First In First Out)

FIFO是硬件/软件实现的缓存队列,核心作用是:

  • 解决"数据产生速度"和"数据消费速度不匹配"的问题(如CIC/DMA传输快,上位机读取慢);
  • 按"先进先出"规则存储数据,支持"满/空/半满"中断;
  • 关键特性:有固定缓存深度(如128字节、1KB),存满后会停止接收或覆盖旧数据。

二、「CIC上行→DMA→FIFO」完整流程

背景:

ATE设备的工位(Site)采集到芯片测试数据(如电压/电流/通道状态),需要通过CIC模块上行上报给主控CPU,主控再将数据发给上位机。硬件设计为:CIC→DMA→FIFO(缓存)→主控CPU→上位机。

完整流程(逐环节拆解):
复制代码
工位传感器 → CIC模块(协议封装) → DMA(数据搬运) → FIFO(缓存) → CPU(读取处理) → 上位机
步骤1:CIC模块的上行数据产生
  • 工位传感器采集到1组测试数据:{通道1:0x12, 通道2:0x34, 工位ID:0x01, 状态:0x00}(共4字节,按自定义协议封装);
  • CIC模块收到传感器的原始数据后,做2件事:
    ① 按通信协议打包(如添加帧头0xAA、帧尾0x55,最终数据包为0xAA 12 34 01 00 55,共6字节);
    ② 触发"上行数据就绪"信号,通知DMA控制器:"有数据需要传输"。
步骤2:DMA驱动接管数据传输
  • DMA已提前配置:
    ✅ 源地址:CIC模块的上行数据寄存器地址(如0x40010000);
    ✅ 目的地址:FIFO的写入地址(如0x40020000);
    ✅ 传输规则:"按包传输"(每6字节触发1次传输)、传输完成后触发DMA中断;
  • DMA无需CPU参与,直接从CIC寄存器读取6字节数据包,写入FIFO缓存;
  • 传输完成后,DMA向CPU发送"传输完成中断",告知:"1包数据已写入FIFO"。
步骤3:FIFO缓存数据
  • FIFO配置:深度为1KB(可存166包6字节数据)、"半满中断"(存满512字节触发);
  • DMA写入的6字节数据包进入FIFO,FIFO的"写入指针"后移6位,当前缓存数据量=6字节(1包);
  • 此时FIFO处于"非满/非半满"状态,未触发任何中断。
步骤4:CPU读取FIFO数据并上报
  • CPU收到DMA中断后,从FIFO的读取地址读取6字节数据包;
  • 解析数据包(去掉帧头帧尾,提取有效数据),通过以太网发送给上位机;
  • 读取完成后,FIFO的"读取指针"后移6位,缓存数据量清零。

三、"FIFO缓存一包数据导致通讯问题"的核心原因

通讯出问题的本质是:FIFO的缓存规则、DMA的传输规则、CPU的读取规则不匹配,导致"数据滞留FIFO"或"数据覆盖/丢失"。以下是3个最典型的案例:

案例1:FIFO"按包缓存"但CPU"按字节读取"→ 数据拆包/粘包
问题场景:
  • DMA按"6字节一包"将数据写入FIFO(1包=6字节);
  • 上位机要求"按字节流式读取"(每次读1字节),但FIFO配置为"只有存满N包才触发中断"(如存满2包=12字节才通知CPU);
  • 实际情况:CIC只上报了1包数据(6字节),FIFO未触发中断,CPU不知道有数据,数据一直滞留在FIFO中→ 上位机收不到数据,通讯超时。
具体表现:
  • 上位机日志:"等待工位1数据超时,未收到任何字节";
  • 硬件调试:FIFO中缓存了1包6字节数据,但读取指针未移动,CPU未读取。
案例2:FIFO缓存深度太小+DMA连续发包→ 数据覆盖
问题场景:
  • FIFO深度仅8字节(只能存1包6字节,剩余2字节空间);
  • CIC连续上报2包数据(共12字节),DMA按包写入FIFO:
    ① 第1包6字节写入FIFO,占用6字节,剩余2字节;
    ② 第2包6字节写入时,FIFO仅剩2字节空间,硬件触发"FIFO满",新数据覆盖第1包的后4字节→ 数据错乱。
具体表现:
  • 上位机收到第1包数据:0xAA 12 34 01 00 55 → 正确;
  • 上位机收到第2包数据:0xAA 56 78 01 00 55(实际应为0xAA 56 78 01 01 55),但FIFO覆盖后变成0xAA 56 78 56 78 55→ 数据错误。
案例3:DMA"传输完成中断"被屏蔽→ FIFO缓存数据无通知
问题场景:
  • 工程调试时,开发人员误屏蔽了DMA的"传输完成中断";
  • DMA正常将1包数据写入FIFO,但CPU未收到任何中断,不知道FIFO中有数据;
  • FIFO一直缓存这1包数据,直到下一包数据写入触发"满中断",才被CPU发现→ 通讯延迟/数据堆积。
具体表现:
  • 上位机收数延迟:第1包数据延迟到第2包写入后才收到,且2包数据粘在一起;
  • 硬件寄存器读取:FIFO的"数据计数"寄存器显示为6字节(1包),但CPU未读取。

四、问题定位与解决思路(工程实操)

1. 先确认各模块的配置(关键调试点)
模块 必查配置项 排查方法
CIC 上行数据包长度、帧头帧尾、触发条件 读取CIC的"数据就绪寄存器",确认数据包格式
DMA 源/目的地址、传输长度、中断使能 查看DMA控制寄存器,单步触发DMA传输验证
FIFO 缓存深度、中断阈值(满/空/半满)、读写指针 读取FIFO的"数据计数寄存器""状态寄存器"
2. 典型解决方法
问题类型 解决思路
数据滞留FIFO ① 降低FIFO中断阈值(如"1包数据即触发中断");② 启用DMA的"单包中断",而非"多包中断"
数据覆盖 ① 增大FIFO缓存深度(如从8字节→1KB);② 配置DMA"FIFO未满才传输",避免强行写入
中断屏蔽/丢失 ① 检查中断掩码寄存器,启用DMA/FIFO中断;② 添加"超时检测",定时扫描FIFO数据
拆包/粘包 ① 统一数据读写规则(CPU按包读取,而非按字节);② FIFO添加"包边界标记"(如每包末尾加0x55)

五、总结

「CIC→DMA→FIFO」的核心是**"数据传输节奏匹配"**:

  • CIC负责"按协议造数",DMA负责"高效搬数",FIFO负责"缓冲存数";
  • 通讯问题的根源几乎都是"节奏错配":要么FIFO的中断阈值太高(1包数据不触发),要么DMA传输规则与FIFO容量不匹配,要么CPU未及时响应中断;
  • 调试时先抓"FIFO数据计数寄存器"(看是否有滞留数据),再查"DMA中断状态"(看是否通知CPU),最后验证"CIC数据包格式"(看数据本身是否正确),三步即可定位90%的问题。

附录

一、DMA预读机制的核心作用

DMA的"预读机制"(也叫预取/预加载机制 )是硬件层面为了提升数据传输效率设计的优化策略,核心目的是:

  • 解决"CIC数据产生速度"与"DMA传输速度"的微小差异,避免DMA频繁等待CIC数据就绪;
  • 利用"批量连续传输"特性(DMA的核心优势),减少总线占用次数,提升整体吞吐率;
  • 典型类比:你去超市买水,"预读"就是一次性拿一箱(预读多份),而非喝一瓶买一瓶(单次读取),效率更高。
技术层面的核心逻辑:

DMA控制器在收到"CIC数据就绪"信号后,不会只读取当前就绪的1份数据,而是根据配置的"预读深度"(如2/4/8个数据单元),提前从CIC的数据源(数据寄存器/缓冲区)读取多份数据到自身的"预读缓冲区",后续按顺序转发到FIFO/内存,无需每次都等CIC的"就绪信号"。

二、预读数据硬件存储位置

预读的数据并非直接到FIFO/内存 ,而是先存到DMA控制器内部的硬件预读缓冲区(Pre-read Buffer) ------这是DMA模块专属的片上缓存(大小通常是几字节到几十字节,如8/16/32字节,由硬件设计决定)。

完整数据路径
复制代码
CIC模块的硬件缓存 → DMA预读缓冲区(预读机制的核心存储) → DMA传输通道 → FIFO/内存
三、处理预读中的数据问题

写寄存器清空CIC Data Buffer"清CIC缓存",预读数据已经脱离CIC缓存,进入了DMA的预读缓冲区------这是两个物理上独立的硬件模块缓存,清CIC缓存只能清空"CIC还没被DMA读取的数据",但管不到"已经被DMA预读走、存在DMA自身缓冲区里的数据"。

四、DMA预读机制的典型配置
1. 预读机制的常见配置项(寄存器层面)
配置项 作用
DMA_PR_DEPTH(预读深度) 设置单次预读的数据单元数(如2/4/8字节/包),深度越大,预读数据越多
DMA_PR_EN(预读使能) 开启/关闭预读机制(关闭后DMA仅读取当前就绪数据,无预读)
DMA_PR_CLR(预读清空) 专门清空DMA预读缓冲区的寄存器(解决"清CIC缓存无效"的关键)
2. 解决"预读数据无法清理"的实操步骤

如果想彻底清理错误数据(包括预读中的数据),正确流程是:

复制代码
Step1:暂停DMA传输(写DMA_CTRL寄存器,置位DMA_PAUSE位)→ 防止DMA继续传输预读数据;
Step2:清空CIC缓存(写CIC_CLR寄存器)→ 清理CIC中未被预读的残留数据;
Step3:清空DMA预读缓冲区(写DMA_PR_CLR寄存器)→ 清理已经预读的错误数据(核心步骤);
Step4:清空FIFO(写FIFO_CLR寄存器)→ 清理已经传输到FIFO的错误数据;
Step5:重启DMA传输(置位DMA_RUN位)→ 恢复正常传输。
3. 预读机制的典型问题
坑点 表现 解决思路
预读深度设置过大 少量错误数据被预读,导致大量错误数据传输到FIFO,通讯错误持续多次 降低预读深度(如从4→1),或关闭预读(仅在数据可靠性优先时用)
预读缓冲区无清空接口 硬件未设计DMA_PR_CLR寄存器,无法清空预读数据 触发DMA"传输中止"(DMA_ABORT),硬件会自动清空预读缓冲区(需确认硬件手册)
预读与CIC中断不同步 CIC触发"数据就绪中断"后,DMA预读了多余数据,导致数据粘包 配置DMA"仅在CIC中断触发时预读",而非"主动轮询预读"
相关推荐
@淡 定2 小时前
缓存更新策略深度解析与最佳实践
缓存
快乐的划水a2 小时前
多级缓存架构
缓存·架构
Irene19912 小时前
HTTP 缓存详解
http·缓存
川石课堂软件测试5 小时前
软件测试的白盒测试(二)之单元测试环境
开发语言·数据库·redis·功能测试·缓存·单元测试·log4j
amunamuna5 小时前
日常梳理-DNS缓存
缓存
H_BB5 小时前
LRU缓存
数据结构·c++·算法·缓存
此生只爱蛋16 小时前
【Redis】Set 集合
数据库·redis·缓存
于归pro20 小时前
Redis 基础命令、核心概念与安装验证完整指南
数据库·redis·缓存
西贝爱学习1 天前
【Redis安装】Redis压缩包Redis-x64-5.0.14.1.zip
数据库·redis·缓存