SNPS I2C简介

8,698 字29 分钟

SNPS I2C简介

SNPS I2C简介

关键术语

  • Transmitter:数据发送方,可能是正在执行写任务的master,也可能是正在被读的slave,关键看数据流向
  • Receiver:数据接收方,可能是正在执行读任务的master,也可能是正在被写的slave,关键看数据流向
  • Master:动作发起者,无论是读亦或写任务,一定是Master主动发起的,负责产生START/STOP command和SCL时钟
  • Slave:The device addressed by the master
  • Multi-master:支持多个master设备可以在一条传输线上共存,且不会出现冲突或数据丢失
  • Arbitration:一条总线上存在多个Master设备时,按照预定义的优先级仲裁,实现每个时间点只有一个master在控制总线
  • Synchronization:总线上多个Master时钟不同步时,按照预先定义的同步时钟
  • STRAT(RESTART):数据传输总是从START或RESTART信号开始,在总线上的表现为:SCL线保持为1时,SDA线从1变成0。当START或RESTART信号出现后,总线进入busy状态
  • STOP:数据传输会被STOP信号终止,在总线上的表现为:SCL线保持为1时,SDA线从0变成1。当STOP信号出现后,总线进入idle或free状态
  • SS:Standard Speed
  • FS:Fast Speed
  • FM+:Fast-Mode Plus
  • HS:High Speed(HS) master

协议基础

  • 用“总线+地址”区分设备,只需要两根线:
    • SCL(Serial Clock):时钟线
    • SDA(Serial Data):数据线
  • 物理层核心设计:open-drain+上拉电阻,SDA/SCL通过一个上拉电阻连接到VDD,通过NMOS管连接到地,Controller连接到NMOS管的栅极。当栅极电平为高时,NMOS导通,SDA/SCL接地,表现为0;当栅极电平为低时,NMOS高阻,SDA/SCL上拉到VDD,表现为1。在这种设计下,多个Controller逻辑表现为线与,任一Controller输出到栅极电平为1时,SDA/SCL=0,只有当所有Controller输出到栅极电平均为0时,SDA/SCL=1,不会出现多驱的问题。这种设计有个问题就是传输速率受限于RC时间常数。
              VDD
               │
              ┌┴┐
              │ │  Rp  ← 唯一上拉电阻(弱的,给"1")
              └┬┘
               │
      ─────────┼───────────────── SDA 走线 ─────────
               │              │              │
            ┌──┴──┐        ┌──┴──┐        ┌──┴──┐
            │ NMOS│        │ NMOS│   ...  │ NMOS│
            └──┬──┘        └──┬──┘        └──┬──┘
               │              │              │
              GND            GND            GND
             (master)       (slave1)        (slaveN)
  • 支持多主多从:Master先发地址再发数据,每个Slave都先获取地址,如果与自身地址匹配则返回响应
  • Start/Stop信号:在SCL为高时动SDA,下降沿表示Start,上升沿表示Stop
  • 数据传输遵从8-bit+第九个ack,高位MSB在前
    • 发送方发完8-bit后释放SDA
    • 接收方拉低SDA=ACK(收到)
    • 接收方保持高=NACK(不要了/结束)
  • 典型事务流程
    • 写START → [addr 7bit + W(0)] → ACK → [数据] → ACK → ... → STOP
    • 读START → [addr + W] → ACK → [片内地址] → ACK → RESTART → [addr + R(1)] → ACK → [数据] → ACK → STOP image
  • 地址:7/10-bit+方向位
    • Start后第一字节:高7位=Slave地址,最低位=R/W(0写/1读)
    • 10-bit寻址用两字节编码
  • 两个高级机制:
    • 时钟拉伸(clock strecthing):Slave拉低SCL不放,Master抬不起来,只能等
    • 仲裁(arbition):多Master抢线,谁输出1却读到0谁输

整体架构

image

  • AMBA Bus Interface Unit:APB Slave,接收配置信息
  • Register File:配置寄存器,与软件的接口
  • Slave State Machine:Slave状态机,作为Slave时运行的状态机
  • Master State Machine:Master状态机,作为Master时运行的状态机
  • Clock generator:生成SCL、检测空闲、Start/Stop、数据建立保持
  • Rx Shift:接收串行数据,转换为并行数据
  • Tx Shift:将内部并行数据串化并发送
  • Rx Filter:事件检测(start/stop/仲裁丢)+尖峰滤波
  • Toggle:跨时钟域传输脉冲信号
  • Synchronizer:跨时钟域同步信号
  • DMA Interface:DMA握手接口
  • Interrupt Controller:原始中断+标志
  • RX FIFO:接收FIFO
  • TX FIFO:发送FIFO

寻址方法

  • 7-bit:首字节高7位=地址,最低位=R/W image
  • 10-bit:两字节发送。首字节=11110+A9:A8+R/W,次字节=A7:A0。11110是10-bit寻址标志 image
  • 特殊首字节关键项:
    • 0000000 0 General Call,广播
    • 0000000 1 START byte
    • 0000001 X CBUS(忽略)
    • 00001XX X HS master code(高速主机码)
    • 11110XX X 10-bit寻址标志,其余为SMBus地址

收发协议

  • Master作为Transmitter
    1. 配置IC_TAR寄存器
    2. 填充TX FIFO,当FSM检测到FIFO非空时,发送START
    3. 按照IC_TAR寄存器内容发送Slave Address
    4. 解析TX FIFO中pop出的9-bit命令1-bit cmd + 8-bit data中的cmd,当前情况下cmd应为0(write)
    5. 收到Slave-receiver返回的ACK信号,开始传输data image
  • Master作为Receiver
    1. 配置IC_TAR寄存器
    2. 填充TX FIFO,当FSM检测到FIFO非空时,发送START
    3. 按照IC_TAR寄存器内容发送Slave Address
    4. 解析TX FIFO中pop出的9-bit命令1-bit cmd + 8-bit data中的cmd,当前情况下cmd应为1(read)
    5. 收到Slave-transmitter返回的ACK信号,开始接收DATA,存到RX FIFO中 image

TX FIFO管理Start/Stop/Restert

按IC_EMPTYFIFO_HOLD_MASTER_EN分两种类型:

=0:FIFO空即发Stop

  • IC_DATA_CMD只有9-bit,bit8=cmd,bit7:0=data
  • 读/写方向变化自动发Restart(若IC_RESTART_EN=1),否则STOP+START
  • 此时Tx FIFO中填充的内容为1-bit R/W + 8-bit data image
  • 不论是读还是写,Master总是由Tx FIFO驱动,当Tx FIFO为空时,传输结束 image image

=1:FIFO空则拉低SCL挂起等待

  • Stop必须显式请求。IC_DATA_CMD高位:
    • bit10 = restart
    • bit9 = stop
    • bit8 = cmd
    • bit7:0 = data
  • 四种设置对应的行为:
    • STOP=1:发完此字节即STOP结束
    • STOP=0且FIFO有货:继续发下一个字节
    • STOP=0且FIFO空:拉低SCL挂起等新命令
    • RESTART=1(方向要变):先RESTART再发此字节
  • Master读、RESTART前必须先NACK
  • Combined format先写片内地址->RESTART掉头->读。需IC_RESTART_EN=1
  • 此时Tx FIFO中填充的内容为1-bit Restart + 1-bit Stop + 1-bit R/W + 8-bit data image
  • 不论是读还是写,Master总是由Tx FIFO驱动,当Tx FIFO为空但弹出的命令中STOP=0时,传输不会结束,Master会把SCL锁定在低电平,等待Tx FIFO中弹出STOP=1的命令,才是传输结束 image image
  • 可以设置Restart=1的命令,重新设置地址、方向等信息 image image
  • 通过插入STOP=1的命令,把Tx FIFO中的要传输的数据切成多笔背靠背的连续传输,跟Restart的区别就是中间会不会释放总线控制权 image
  • 允许在FIFO Empty空之后的第一个字节就填充Restart=1的命令(其实不是很明白单独强调的意义是什么),强调它的灵活性? image
  • 类似2-18 image
  • 类似2-19 image

多主仲裁+时钟同步+HS主码

  • SDA仲裁:多Master同时在总线上发送数据时,发1却读到0的Master输掉仲裁,关闭SDA和SCL输出并退避。地址大者易输。结果报在IC_TX_ABRT_SOURCE的ARB_LOST
位:   7     6     5     4     3     2     1
A:    1     0     1     0     ...
B:    1     0     1     1     ...
     一致   一致  一致  ↑ 第4位分出胜负
                        A发0,B发1 → B读到0 → B输,退避
  • SCL时钟同步:多Master同时在总线上发送数据时,SCL也同步发送,在未仲裁出结果前,多Master按照自己的计数输出高低电平,由开漏电气特性决定SCL线是高电平或低电平。由于SCL是弱上拉,强下拉,所以有一个Master输出低电平时,SCL表现为低,而所有Master输出高电平时,SCL才表现为高,低周期由最慢master决定,高周期由最快决定
CLKA(快): 0 1 1 0 0 1 1 0 0
CLKB(慢): 0 0 0 0 1 1 1 1 0
SCL(与):  0 0 0 0 0 1 1 0 0
            └─A 已放手,干等 B
                  └─A 先拉低,B 的高被截断
  • HS mode主码:先以标准/快模式速度发00001XXX(HS master mode)才切高速。存IC_HS_MADDR,对应Master FSM的TX_HS_MCODE状态

操作模式

如何作为Slave工作

初始化

  1. 禁用IP,写IC_ENABLE寄存器的bit0为0
  2. 配置IP响应地址,写IC_SAR寄存器的bit9:0
  3. 写IC_CON寄存器的bit3以明确地址类型(7/10-bit)
  4. 写IC_CON寄存器的bit6(IC_SLAVE_DISABLE)和bit0(MASTER_MODE)为0以保证IP工作在slave-only mode
  5. 使能IP,写IC_ENABLE寄存器的bit0为1

Single Byte Transmitter

  1. Master发起transfer,地址与本地响应地址匹配
  2. Slave确认收到地址,识别transfer的方向,确认担任slave-transmitter角色
  3. IP拉高RD_REQ中断(IC_RAW_INTR_STAT寄存器的bit5),保持SCL线常低,等待软件响应。如果由于IC_INTR_MASK寄存器(M_RD_REQ域)被置0而导致RD_REQ中断被屏蔽,建议采用硬件和/或软件定时程序,指示CPU定期读取IC_RAW_INTR_STAT寄存器。
    1. 若读取结果显示IC_RAW_INTR_STAT[5](R_RD_REQ 域)被置 1,则必须将其视为等同于 RD_REQ 中断被触发
    2. 软件随后必须执行相应操作以完成 I2C 传输
    3. 所使用的定时间隔应约为IP可处理的最快 SCL 时钟周期的 10 倍。例如,对于 400 kb/s 的速率,定时间隔为 25 微秒
  4. 如果在收到读取请求前,TX FIFO 中仍有残留数据,则 DW_apb_i2c 会触发 TX_ABRT 中断(IC_RAW_INTR_STAT的bit6),以清空 TX FIFO 中的旧数据。如果由于 IC_INTR_MASK寄存器(M_TX_ABRT 位域)被置 0 而屏蔽了 TX_ABRT 中断,建议复用类似例程来读取 IC_RAW_INTR_STAT寄存器。
    1. 若读取结果显示bit6(R_TX_ABRT)被置为1,则必须将其视为等同于TX_ABRT中断被触发
    2. 软件无需执行任何额外操作
    3. 所使用的时间间隔应与上一步骤中针对IC_RAW_INTR_STAT[5]寄存器所述的时间间隔相近。
  5. 软件将要传输的数据写入IC_DATA_CMD寄存器,bit8写0
  6. 软件在传输前必须要分别清除IC_RAW_INTR_STAT中的RD_REQ和TX_ABRT。如果 RD_REQ 和/或 TX_ABRT中断已被屏蔽,则在读取到R_RD_REQ或R_TX_ABRT为1时,即已完成对 IC_RAW_INTR_STAT 寄存器的清零操作。
  7. IP释放对SCL线的持续拉低,并开始传输字节
  8. 主机可通过发送RESTART来持续占用I2C总线,或通过发送STOP来释放总线。

Single Byte Receiver

  1. Master发起transfer,地址与本地响应地址匹配
  2. Slave确认收到地址,识别transfer的方向,确认担任slave-receiver角色
  3. Slave接收传输数据并填充到Rx FIFO中。
    • 需要正确处理Rx FIFO的溢出情况:如果IC_RX_FULL_HLD_BUS_EN=0,需要软件快速响应溢出中断,同时Rx FIFO的深度(应该是水位线)应该设置大一点,能够保证软件响应中断过程中不会丢数据;如果IC_RX_FULL_HLD_BUS_EN=1,且IC_CON寄存器的bit9RX_FIFO_FULL_HLD_CTRL设置为1,则Slave可以hold SCL线为low,直到Rx FIFO有低于水位线
  4. IP会触发 RX_FULL中断(即 IC_RAW_INTR_STAT[2] 寄存器对应的中断)。若因将 IC_INTR_MASK[2] 寄存器设为 0 或设置 将 IC_TX_TL 设置为大于 0 的值,建议采用定时例程
  5. 软件可以从IC_DATA_CMD读数据
  6. 其它主机可通过发送RESTART来占用I2C总线,或通过发送STOP来释放总线。

Bulk传输

  • 硬件侧不需要做什么配置或改动,Bulk传输是为软件侧提供的高性能使用方法
  • 如果软件提前知道Master要读n字节,就一次性把这n字节都塞进Tx FIFO,之后从机就从FIFO中一个接一个地弹字节发出去,只要FIFO中还有内容,就不拉低SCL,绕开了每发一个字节就拉起RD_REQ中断请求CPU发数据的延迟
  • 硬件侧可能出现三种情况:
    • 正常:FIFO一直有内容,master一直ACK,不拉低SCL,不产生中断,CPU完全不用管
    • 异常1:FIFO空了,但是master还在ACK(继续要数据),自动退化为标准模式,CPU按byte给数据,I2C传完继续拉高RD_REQ等数据
    • 异常2:FIFO还有数据,但是master返回NACK(不要数据了),IP侧设置一个标志,跨时钟域传到处理器侧,控制IP清理FIFO中的数据,并触发一个TX_ABRT事件,告诉软件

如何作为Master工作

初始化

有两种配置,根据配置parameterI2C_DYNAMIC_TAR_UPDATE区分

I2C_DYNAMIC_TAR_UPDATE=0(NO)
  1. 禁用IP,写IC_ENABLE的bit0为0
  2. 配置传输速率,写IC_CON寄存器的bit2:1
  3. 配置寻址模式,写IC_CON寄存器的bit4
  4. 确保IC_SLAVE_DISABLE和MASTER_MODE为1,分别对应IC_CON寄存器的bit6和bit0
  5. 写IC_TAR寄存器分配地址,该寄存器还会指示I2C即将执行的是General Call还是START BYTE命令
  6. (仅应用于high-speed模式)写IC_HS_MADDR寄存器所需的master code,由编程人员定义
  7. 使能IP,写IC_ENABLE的bit0为1
  8. 把发送方向和要传输的数据写到IC_DATA_CMD寄存器中。如果在IP被使能前写入,这批数据会丢失,因为IP在被disable时buffers会持续保持clear。当IP检测到使能开启,且Tx FIFO非空时,它就会发起START,开始工作
I2C_DYNAMIC_TAR_UPDATE=1(YES)
  1. 禁用IP,写IC_ENABLE的bit0为0
  2. 配置传输速率,写IC_CON寄存器的bit2:1
  3. 配置寻址模式,写IC_CON寄存器的bit4
  4. 确保IC_SLAVE_DISABLE和MASTER_MODE为1,分别对应IC_CON寄存器的bit6和bit0(为什么不用这个)
  5. 写IC_TAR寄存器分配地址,该寄存器还会指示I2C即将执行的是General Call还是START BYTE命令
  6. 由IP发起的传输,无论是7-bit还是10-bit寻址模式,其速率均由IC_10BITADDR_MASTER的bit12决定
  7. (仅应用于high-speed模式)写IC_HS_MADDR寄存器所需的master code,由编程人员定义
  8. 使能IP,写IC_ENABLE的bit0为1
  9. 把发送方向和要传输的数据写到IC_DATA_CMD寄存器中。如果在IP被使能前写入,这批数据会丢失,因为IP在被disable时buffers会持续保持clear。

IC_TAR和IC_10BITADDR_MASTER动态更新

  • 软件保证Tx FIFO中无依赖TAR寄存器的命令,即不会再STOP或Restart,剩下的都是连续的读或者写,这时可以动态更新IC_TAR寄存器
  • 如果无法保证第一条,则需满足以下条件之一才能动态更新:
    • 关闭:IP未使能IC_ENABLE[0]=0
    • 空闲+空仓:IP使能IC_ENABLE[0]=1 & IP未参与任何Master操作IC_STATUS[5]=0 & IP使能工作于Master模式IC_CON[0]=1 & Tx FIFO中无条目IC_STATUS[2]=1
  • 如果想换地址,但不想释放总线,需满足以下条件方可动态更新地址:
    • IP使能IC_ENABLE[0]=1
    • 正确的参数配置IC_EMPTYFIFO_HOLD_MASTER_EN=1
    • IP使能工作于Master模式IC_CON[0]=1
    • Tx FIFO无条目且master处于HOLD状态IC_INTR_STAT[13]=1

关机

写IC_ENABLE[0]=0只是提交了关机申请,硬件需要完成当前任务后才关闭,如何确认关闭:

  1. 定一个轮询间隔tic2_poll,等于最高传输速率下一笔传输所用时间的10倍,400kHz下为25us
  2. 定一个最大次数MAX_T_POLL_COUNT,设置轮询上限,防止卡死
  3. 阻塞软件,不再发起新传输
  4. 计数器清零
  5. 写IC_ENABLE[0]=0
  6. 轮询循环
读 IC_ENABLE_STATUS[0], POLL_COUNT++
如果 POLL_COUNT 超过上限,报错推出(总线可能卡死)
如果还是1 -> 睡 ti2c_poll,回去再读
如果变0 -> 关完了,成功退出

如果IC_EMPTYFIFP_HOLD_MASTER_EN=1,关机申请到达时,正在处理的命令没有STOP,且Tx FIFO空掉,软件也不给数据了,master就会拉低SCL一直等新命令,导致卡死。这种情况需要先发ABORTIC_ENABLE[1]=1强制释放总线,再走关闭流程。

Aborting

打断传输过程。当Master接收到Aborting Request时,它会在I2C总线上发出STOP,同时清理掉Tx FIFO中剩余的数据

  1. 停止填充Tx FIFO,即不再写IC_DATA_CMD
  2. 如果是使用DMA填充数据,则置TDMAE=0来disable
  3. 写IC_ENABLE的bit1为1
  4. 等待M_TX_ABRT中断
  5. 读IC_TX_ABRT_SOURCE寄存器,确认中断源是ABRT_USER_ABRT

尖峰抑制

  • SDA/SCL输入端各一个计数器型数字滤波器:输入连续稳定N个ic_clk周期才被采样认可

时钟配置

  • I2C的时钟信号SCL比较特殊,它不是严格的50%占空比的方波,而是一个由ic_clk计数的“高电平时长+低电平时长”可独立编程的波形。
  • IP中依赖两个计数器对时钟信号进行编程:IC_*_SCL_HCNT和IC_*_SCL_LCNT
  • 时钟发生器DW_apb_i2c_clk_gen.v拉低SCL->数LCNT->放手等它变高->数HCNT->再拉低,构成时钟信号SCL的上升下降沿

参数计算例子

  • 要求:SoC提供时钟源为50MHz,即ic_clk固定为50MHz,周期为20ns;I2C工作于Fast Mode,即scl时钟频率为400kHz,周期为2500ns
  • 第一步:看Fast Mode的协议参数
参数 协议值 作用
tLOW,min 1300ns 低段下限
tHIGH,min 600ns 高段下限
tr,max 300ns 允许的最大上升时间
tSP 50ns 必须滤掉的毛刺宽度
tSU;DAT,min 100ns 数据建立时间下限
  • 第二步:算SPKLEN
    • IC_FS_SPKLEN=ceil(tSP/拍)=ceil(50/20)=3
  • 第三步:分配空拍。由于高段有上拉电阻的RC充电斜坡,tr最大允许300ns(15拍),多给高段20拍,作为留给tr慢慢爬的余量
周期 = 2500ns = 125拍
低段 >= 1300ns = 65拍
高段 >= 600ns  = 30拍
空拍 = 125 - 65 - 30 = 30拍 -> 20拍给高段,10拍给低段
波形目标:低=75拍,高=50拍
  • 第四步:换算成寄存器写入值。硬件本身有几个固定开销拍
IC_FS_SCL_LCNT = 低段拍数 - 1 = 75 - 1 = 74
IC_FS_SCL_HCNT = 高段拍数 - (SPKLEN + 7) = 50 - (3 + 7) = 40
  • 第五步:约束SDA数据时序。
IC_SDA_HOLD[15:0] (TX保持) = 1 ~ 4    约束:必须< LCNT - 2 = 72
IC_SDA_HOLD[23:16] (RX保持) = 0       约束:必须< HCNT - SPKLEN - 3 = 40 - 3 - 3 = 34。FS不需要桥接,0就OK了
IC_DSA_SETUP = 5                     约束:tSU;DAT = 100ns = 5拍,SDA必须在SCL上升前至少稳定这么久
  • 第六步:配置IC_CON寄存器的bit2:1为2'b10,工作于FS模式

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
SNPS I2C简介https://bedic.tech/wiki/snps-i2c简介
作者
Bed
发布于
2026-10-21
许可协议
保留所有权利

随机文章

评论区

分享你的想法,与大家交流讨论

正在加载评论…