功能简介
本文基于Xilinx Virtex Ultrascale+HBM VCU128 FPGA开发板,通过利用开发板上的TCA6416A芯片,对I2C通信方式进行学习。
根据VCU128用户手册,128中具有两条I2C总线,其中一条连接有4个I2C芯片,能够与系统监视器(SYSMON)、EEPROM、QSFP等硬件资源进行通信。
根据手册介绍,TCA6416A芯片为I/O拓展芯片,PCA9544A、PCA9548A芯片为1-4、1-8通道总线交换芯片。两块TCA9548A芯片的使用需要先保证TCA6416A芯片的ICC_MUX_RESET-B引脚为高阻态或高电平,因此本文对利用FPGA控制TCA6416A芯片的I2C通信进行介绍。
在实际使用中,Xilinx配置TCA6416A芯片的ADDR引脚为0,在FPGA上访问TCA6416A芯片的SCL、SDA引脚可通过如下约束设置,对应BANK67的BL28、BM27引脚。
set_property BOARD_PART_PIN IIC0_SCL_MAIN [get_ports i2c_0_scl]
set_property BOARD_PART_PIN IIC0_SDA_MAIN [get_ports i2c_0_sda]
TCA6416A芯片介绍
TCA6416A为TI公司生产的I2C转16比特I/O拓展芯片,芯片手册,该芯片能够利用I2C协议进行16个通用引脚(分为2组:P00-P07,P10-P17)的读写访问,每个引脚支持设置为输入、输出两种模式。
ADDR引脚用于设置TCA6416A的I2C通信地址,如下图所示,TCA6416A的地址包含一个可编程位,在VCU128中,该位被设置为0。
TCA6416A的I/O Port为2组8比特通用IO引脚,在通过I2C通信进行使用时会通过8种指令读写8个寄存器,进而实现IO引脚的间接访问。这些操作会通过组的形式对2组8比特IO引脚进行访问。
在使用时,TCA6416A的I2C总线包含标准模式、快速模式两种,其中标准模式下SCL的时钟频率不能超过100KHz,快速模式下SCL的时钟频率不能超过400KHz。本文使用标准模式,时钟频率100KHz。
I2C发送端逻辑实现
I2C是一种采用报文方式传输的串行通信方式,每次发送过程包含一个发送端和一个接收端。从发送端角度看由输出引脚SCL与输入输出引脚SDA构成,其中SDA为每次传输的数据,SCL为代表每次传输数据有效的高电平有效选通信号(时钟信号)。除了报文的开始位和结束位以外,SDA只能在SCL为低电平时发生变化,在SCL为高电平时应保持不变。
I2C传输的报文格式如下,包含1个开始位(S)、8个数据位(1个字节)、1个确认位(ACK)、1个停止位(P)。开始位由发送端发送,在发送完成后,可以进行多次数据位、确认位的交替传输,在最终交互完成后,由发送端发送停止位表示本次通信结束。在发送端发送完8个字节数据后,接收端会占用SDA发送确认位用于表明当前状态,类似一种响应信号。发送端可以根据接收端发送的确认位决定下一步的行动。
对于TCA6416A芯片,I2C传输可分为两种类型的操作。
写操作
根据手册,对于TCA6416A芯片的写操作格式如下,Target Address为TCA6416A芯片的地址,Command Byte为本次访问的操作名称,后续Data0、Data1为写入的数据。下图展示了利用0x20指令写端口0的指令。
下面展示了进行I2C写操作的状态机:
always_comb begincase (fsm_r)RESET: beginif (itf_rst_done) beginfsm_s = IDLE;end else beginfsm_s = RESET;endendIDLE: beginif (single_bit_hold_flag_r) beginif (START_flag_r) beginfsm_s = ADDR_START;end else beginfsm_s = IDLE;endend else beginfsm_s = IDLE;endendADDR_START: beginif (single_bit_hold_flag_r) beginfsm_s = ADDR_DATA;end else beginfsm_s = ADDR_START;endendADDR_DATA: beginif (single_bit_hold_flag_r && pos_cnt_r == 4'd7) beginfsm_s = ADDR_ACK;end else beginfsm_s = ADDR_DATA;endendADDR_ACK: beginif (single_bit_hold_flag_r) begin// if (STOP_flag_r) beginif (1'b0) beginfsm_s = ADDR_STOP;end else beginfsm_s = CMD_DATA; // ADDR_STOP;endend else beginfsm_s = ADDR_ACK;endendADDR_STOP: beginif (single_bit_hold_flag_r) beginfsm_s = IDLE;end else beginfsm_s = ADDR_STOP;endendCMD_DATA: beginif (single_bit_hold_flag_r && pos_cnt_r == 4'd7) beginfsm_s = CMD_ACK;end else beginfsm_s = CMD_DATA;endendCMD_ACK: beginif (single_bit_hold_flag_r) begin// if (STOP_flag_r) beginif (1'b0) beginfsm_s = CMD_STOP;end else beginif (RWn_flag_r) beginfsm_s = SECADDR_START; // ADDR_STOP;end else beginfsm_s = WR_DATA; // ADDR_STOP;endendend else beginfsm_s = CMD_ACK;endendCMD_STOP: beginif (single_bit_hold_flag_r) beginfsm_s = IDLE;end else beginfsm_s = CMD_STOP;endendWR_DATA: beginif (single_bit_hold_flag_r && pos_cnt_r == 4'd7) beginfsm_s = WR_ACK;end else beginfsm_s = WR_DATA;endendWR_ACK: beginif (single_bit_hold_flag_r) beginif (STOP_flag_r) beginfsm_s = WR_STOP;end else beginfsm_s = WR_DATA;endend else beginfsm_s = WR_ACK;endendWR_STOP: beginif (single_bit_hold_flag_r) beginfsm_s = IDLE;end else beginfsm_s = WR_STOP;endenddefault: beginfsm_s = RESET;endendcaseend
读操作
TCA6416A芯片的读操作格式如下,在进行读操作前,需要先通过写操作设置指令。使得TCA6416A芯片得知后续对哪个寄存器进行读操作。之后再利用开始位(S)重启读操作。下图展示了利用0x20指令读端口0的I2C格式。
在读操作中,在FPGA进行I2C读操作时,后续I2C总线将由TCA6416A芯片发送8个数据位,由FPGA发送确认位(ACK),如下图所示。
下面展示了进行I2C读操作的状态机:
always_comb begincase (fsm_r)RESET: beginif (itf_rst_done) beginfsm_s = IDLE;end else beginfsm_s = RESET;endendIDLE: beginif (single_bit_hold_flag_r) beginif (START_flag_r) beginfsm_s = ADDR_START;end else beginfsm_s = IDLE;endend else beginfsm_s = IDLE;endendADDR_START: beginif (single_bit_hold_flag_r) beginfsm_s = ADDR_DATA;end else beginfsm_s = ADDR_START;endendADDR_DATA: beginif (single_bit_hold_flag_r && pos_cnt_r == 4'd7) beginfsm_s = ADDR_ACK;end else beginfsm_s = ADDR_DATA;endendADDR_ACK: beginif (single_bit_hold_flag_r) begin// if (STOP_flag_r) beginif (1'b0) beginfsm_s = ADDR_STOP;end else beginfsm_s = CMD_DATA; // ADDR_STOP;endend else beginfsm_s = ADDR_ACK;endendADDR_STOP: beginif (single_bit_hold_flag_r) beginfsm_s = IDLE;end else beginfsm_s = ADDR_STOP;endendCMD_DATA: beginif (single_bit_hold_flag_r && pos_cnt_r == 4'd7) beginfsm_s = CMD_ACK;end else beginfsm_s = CMD_DATA;endendCMD_ACK: beginif (single_bit_hold_flag_r) begin// if (STOP_flag_r) beginif (1'b0) beginfsm_s = CMD_STOP;end else beginif (RWn_flag_r) beginfsm_s = SECADDR_START; // ADDR_STOP;end else beginfsm_s = WR_DATA; // ADDR_STOP;endendend else beginfsm_s = CMD_ACK;endendCMD_STOP: beginif (single_bit_hold_flag_r) beginfsm_s = IDLE;end else beginfsm_s = CMD_STOP;endendSECADDR_START: beginif (single_bit_hold_flag_r) beginfsm_s = SECADDR_DATA;end else beginfsm_s = SECADDR_START;endendSECADDR_DATA: beginif (single_bit_hold_flag_r && pos_cnt_r == 4'd7) beginfsm_s = SECADDR_ACK;end else beginfsm_s = SECADDR_DATA;endendSECADDR_ACK: beginif (single_bit_hold_flag_r) begin// if (STOP_flag_r) beginif (1'b0) beginfsm_s = SECADDR_STOP;end else beginfsm_s = RD_DATA; // ADDR_STOP;endend else beginfsm_s = SECADDR_ACK;endendSECADDR_STOP: beginif (single_bit_hold_flag_r) beginfsm_s = IDLE;end else beginfsm_s = SECADDR_STOP;endendRD_DATA: beginif (single_bit_hold_flag_r && pos_cnt_r == 4'd7) beginfsm_s = RD_ACK;end else beginfsm_s = RD_DATA;endendRD_ACK: beginif (single_bit_hold_flag_r) beginif (STOP_flag_r) beginfsm_s = RD_STOP;end else beginfsm_s = RD_DATA;endend else beginfsm_s = RD_ACK;endendRD_STOP: beginif (single_bit_hold_flag_r) beginfsm_s = IDLE;end else beginfsm_s = RD_STOP;endenddefault: beginfsm_s = RESET;endendcaseend
仿真测试
仿真模拟了利用VIO输入0x02端口0引脚读写指令后FPGA的工作过程 。其中蓝色高阻态用于TCA6416A发来的响应信息。
initial begin // write to Output Port Registerforce i2c_top_inst.CMD_vec_r = 8'b00000010; force i2c_top_inst.wrDATA_vec_r = 8'h6a;force i2c_top_inst.START_flag_r = 1'b1;force i2c_top_inst.RWn_flag_r = 1'b1;force i2c_top_inst.wrACK_flag_r = 1'b0;force i2c_top_inst.STOP_flag_r = 1'b1;end
上板测试
这里以0x02端口0引脚读写指令为例,进行I2C通信测试,初始状态下端口0的8个引脚值为0x77。
通过设置读写模式选择RWn_flag_r为0,指令CMD_vec_r为0x20,写数据wrDATA_vec_r为0x6A,点击START_flag_r控制FPGA发送I2C写端口0的8bit为6A。
通过设置RWn_flag_r为1,其余保持不变,点击START_flag_r控制FPGA发送I2C读端口0的8bit,得到rdDATA_vec_r结果为6A。
完整代码
工程代码可于同名公众号回复I2C_TCA6416A下载。