当前位置:首页 > 新闻资讯 > FPGA之家动态 >

vivado技巧|如何在fpga内部实现i2c信号透传(fpga内部两组i2c inout信号互连)

时间:2024-07-29      来源:网络搜集 关于我们 0

     大家好,我是数字小熊饼干,一个练习时长两年半的ic打工人。我在两年前通过自学跨行社招加入了IC行业。现在我打算将这两年的工作经验和当初面试时最常问的一些问题进行总结,并通过汇总成文章的形式进行输出,相信无论你是在职的还是已经还准备入行,看过之后都会有有一些收获,如果看完后喜欢的话就请关注我吧~谢谢~

    最近遇到了这么一个问题:我有一个i2c master,它的管脚与fpga相连接,在fpga内部有一个i2c slave,在fpga外部还有一个i2c slave,但是它的管脚直接与fpga管脚连接了,并没有多余的地方让我能够直接接线和i2c master连接。因此,问题就转换成了——如何在fpga内部实现i2c信号透传,即在fpga内部实现相当于实际用导线直接把两个端口连接在一起的功能。   

一、三态门

   i2c的scl和sda信号的输出都是漏极开路,只能输出低电平和高阻态,且外接了一个上拉电阻,将i2c总线空闲时的输出电平拉至高电平,最终实现了线与的功能。

    而在fpga内部,我们通常将端口处i2c的scl和sda信号定义为inout类型的信号,用verilog代码描述则为:

inout scl; wire scl_out, scl_in; assign scl = (scl_out==1b0) ? 1b0 : 1bz; assign scl_in = scl;

    其中,scl为fpga端口处的scl信号,scl_out为内部输出的scl信号,scl_in为内部输入的scl信号。上述代码会被综合成三态门的形式,即:

    之所以叫三态门,是因为它的第三个状态是高阻态z。在实际电路中高阻态意味着响应的管脚悬空、断开,输出电平既不是0,也不是1,对下级电路无任何影响,和没接一样。当三态门的控制信号EN为真时,三态门导通;控制信号为假时,三态门的输出端是高阻态。

   对于inout端口这种双向IO口,是用了两个三态门来实现的,一个负责输出,一个负责输入,通过EN与EN’作为控制信号来实现一个高阻断路,另一个实现输出/输入。因此,虽然说是双向IO口但是其实它的输入和输出不是同时进行的,需要有一个控制信号EN来控制端口什么时候为输出,什么时候为输入。

    对于我们的i2c scl与sda信号来说,若EN有效,则三态门作为输出使用,输出0值,否则则则为高阻态z,此时则是作为输入使用

二、fpga内部i2c信号的互连

 在了解了三态门实现inout端口的原理之后,我们回到最初的那个问题:如何在fpga内部实现i2c信号透传,即实现相当于实际中用导线直接把两个端口连接在一起的功能。

    其实问题的解决方法很简单!要想实现fpga内部实现i2c信号透传关键在于控制一个inout端口为输入,一个inout端口为输出!也就是如下图所示:

   于是问题又变成了如何控制三态门的使能,以在i2c master输出,i2c slave输入,与i2c master输入i2c slave输出之间来回切换。

    这个问题也很好解决,我们可以通过状态机中i2c地址阶段,地址应答阶段,读阶段,读应答阶段,写阶段,写应答阶段的跳转,结合对应的i2c信号输入是否为0,来对三态门的使能进行控制,直接上代码:

localparam DRIVE_IDLE = 3b000; localparam DRIVE_ADDR = 3b001; localparam DRIVE_ADDR_ACK = 3b010; localparam DRIVE_WR = 3b011; localparam DRIVE_WR_ACK = 3b100; localparam DRIVE_RD = 3b101; localparam DRIVE_RD_ACK = 3b110; reg [2:0] i2c_drive_cs, i2c_drive_ns; reg rd_wt; always @(posedge clk_sys or negedge rst_n) begin if (!rst_n) begin rd_wt <= 1b0; end else if (start_evt) begin rd_wt <= 1b0; end else if ((i2c_drive_cs==DRIVE_ADDR) && (scl_pos_cnt==4d8)) begin rd_wt <= sda_i_sync; end end always @(*) begin if (stop_evt)begin i2c_drive_ns = DRIVE_IDLE; end else if (start_evt && i2c_direct_access_en) begin i2c_drive_ns = DRIVE_ADDR; end else begin i2c_drive_ns = i2c_drive_cs; case(i2c_drive_cs)                //DRIVE_IDLE: i2c_drive_ns = start_evt && i2c_direct_access_en ? DRIVE_ADDR : i2c_drive_cs; DRIVE_ADDR: i2c_drive_ns = (scl_pos_cnt==4d8) && scl_neg ? DRIVE_ADDR_ACK : i2c_drive_cs; DRIVE_ADDR_ACK: i2c_drive_ns = scl_neg ? (rd_wt ? DRIVE_RD : DRIVE_WR ) : i2c_drive_cs; DRIVE_WR : i2c_drive_ns = (scl_pos_cnt==4d8) && scl_neg ? DRIVE_WR_ACK : i2c_drive_cs; DRIVE_WR_ACK: i2c_drive_ns = scl_neg ? DRIVE_WR : i2c_drive_cs; DRIVE_RD: i2c_drive_ns = (scl_pos_cnt==4d8) && scl_neg ? DRIVE_RD_ACK : i2c_drive_cs; DRIVE_RD_ACK: i2c_drive_ns = scl_neg ? DRIVE_RD : i2c_drive_cs;                default: i2c_drive_ns = i2c_drive_cs; endcase end end always @(posedge clk_sys or negedge rst_n) begin if (!rst_n) begin i2c_drive_cs <= DRIVE_IDLE; end else begin i2c_drive_cs <= i2c_drive_ns; end end reg i2c_sda_mst_drive_oe, i2c_sda_drive_oe, i2c_scl_mst_drive_oe; always @(posedge clk_sys or negedge rst_n) begin if (!rst_n) begin i2c_sda_mst_drive_oe <= 1b0; i2c_sda_drive_oe <= 1b0; i2c_scl_mst_drive_oe <= 1b0; end else begin i2c_sda_mst_drive_oe <= ((i2c_drive_ns == DRIVE_ADDR) || (i2c_drive_ns == DRIVE_WR) || (i2c_drive_ns == DRIVE_RD_ACK)) && (sda_i_sync==1b0); i2c_sda_drive_oe <= ((i2c_drive_ns == DRIVE_ADDR_ACK) || (i2c_drive_ns == DRIVE_WR_ACK) || (i2c_drive_ns == DRIVE_RD)) && (sda_master_in_sync==1b0); i2c_scl_mst_drive_oe <= (i2c_drive_ns != DRIVE_IDLE) && (scl_i_sync==1b0); end end assign sda =(i2c_sda_drive_oe) ? 1b0 : 1bz ; assign scl = 1bz ; assign sda_i = sda ; assign scl_i = scl ; assign sda_mst = (i2c_sda_mst_drive_oe) ? 1b0 : 1bz ; assign scl_mst = (i2c_scl_mst_drive_oe) ? 1b0 : 1bz ; assign sda_master_in = sda_mst ; assign scl_master_in = scl_mst ;

    其中scl和sda为输入fpga的i2c master inout端口信号,scl_mstsda_mst为连接fpga外部i2c slave inout端口号。

      使用vivado跑的波形仿真如下图所示:

     第一帧访问fpga内部i2c salve下配置,第二帧为访问fpga部i2c salve进行2c burst读操作第三帧为访问fpga部i2c salve进行i2c burst写操作第四帧为访问fpga部i2c salve进行i2c burst读,i2c读写正常工作。

    然后我们将vivado生成的bit文件烧写到fpga上进行测试,逻辑分析仪ila的结果如下:

    这是一个i2c读序列波形,i2c波形正常,说明我们的代码没有问题。

    如果你喜欢这篇文章的话,请关注我的公众号-熊熊的ic车间,里面还有ic设计和ic验证的学习资料和书籍等着你呢~欢迎您的关注!


注明:本内容来源网络,不用于商业使用,禁止转载,如有侵权,请来信到邮箱:429562386ⓐqq.com 或联系本站客服处理,感谢配合!

用户登陆

    未注册用户登录后会自动为您创建账号

提交留言