移植OPENCORES上的I2C MASTER到AXI接口处理器层面实现驱动 之一

本文介绍如何将采用Wishbone总线标准的I2C Master IP核从OpenCores移植到AXI Lite接口。文章详细讲解了接口转换过程中的注意事项及实现步骤,并提供具体的代码示例。




由于之间就用过OPENCORES上的I2C MASTER,所以对他的接口时序以及处理器方面的驱动非常了解。这里主要做的工作是移植。





这个IP核来自www.opencores.org 是经过OPENCORES认证的IP核。




这个IP是采用了WISHBONE互联总线标准,是最简单最基本的互联总线,因此很容易移植上AXI_LITE接口。以下代码是我以前做的一个项目的例化代码,


 /*
 i2c_master_top U1(
	wb_clk_i ( ), 
	wb_rst_i ( ), 
	arst_i ( ), 
	wb_adr_i ( ), 
	wb_dat_i ( ), 
	wb_dat_o ( ),
	wb_we_i ( ), 
	wb_stb_i ( ), 
	wb_cyc_i ( ), 
	wb_ack_o ( ), 
	wb_inta_o ( ),


	.scl_pad_i ( ), 
	.scl_pad_o ( ), 
	.scl_padoen_o ( ), 
	.sda_pad_i ( ), 
	.sda_pad_o ( ), 
	.sda_padoen_o( )
	);
	*/

我们看到非常简单,下面做的工作就是直接简历AXI_LITE外设实现实例化此模块并将接口处理好。


下面做的主要工作就是接口的转换,将WISHBONE接口转成AXI LITE 接口。

1,WISHBONE只读写地址是一个,而AXI_LITE接口的读写地址是两个分开。这点要对I2C MASTER进行修改。

2,这里I2C_MASTER的WISHBONE用三位地址长度,所以我们也用三位的AXI_LITE地址总线。

3,这里I2C_MASTER的WISHBONE数据位长度是8位,而AXI_LITE 总线是32位数据长度。

4,对于I2C_MASTER来说不会挂起总线,任何时候的读和写都是READY的,因此就简单处理好读写时序就可以。

5,写寄存器时,地址,数据以及写信号同时出现在时钟的上升边缘,就完成写操作,所以我们做接口转换时候找到数据,地址,和写信号就行。

6,读数据的时候,我们加进去读地址,数据在下一周期出现在WB_DOUT。

7,I2C接口的 scl_pad_i, scl_pad_o, scl_padoen_o, sda_pad_i, sda_pad_o, sda_padoen_o 信号直接引出来到顶层处理。

8,wb_stb_i, wb_cyc_i, 两个原来的WISHBONE信号直接接1就可以。


简单修改一下原来WISHBONE接口的顶层文件,加上读寄存器地址,去掉多余的控制信号,接口成:

module i2c_master_top(
	wb_clk_i, wb_rst_i, 
	//arst_i,
	wb_adr_i_wr,wb_adr_i_rd, wb_dat_i, wb_dat_o,
	wb_we_i, 
	//wb_stb_i, wb_cyc_i, 
	//wb_ack_o, 
	wb_inta_o,
	scl_pad_i, scl_pad_o, scl_padoen_o, sda_pad_i, sda_pad_o, sda_padoen_o );

我们直接编写AXI LITE外设,步骤在我给大家的视频已经详细提过,这里注意设置8个寄存器。按照模板生产,之后加入做了简单修改后的2C_MASTER控制代码:

	// Add user logic here
i2c_master_top  u1(
        .wb_clk_i( S_AXI_ACLK  ), 
        .wb_rst_i( ~S_AXI_ARESETN ), 
        .wb_adr_i_wr( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ),
        .wb_adr_i_rd( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ), 
        .wb_dat_i(  S_AXI_WDATA[7:0]), 
        .wb_dat_o( axi_rdata ),
        .wb_we_i( slv_reg_wren  ), 
        .wb_inta_o(  ),
        .scl_pad_i(  scl_pad_i), 
        .scl_pad_o( scl_pad_o ), 
        .scl_padoen_o( scl_padoen_o ), 
        .sda_pad_i( sda_pad_i ), 
        .sda_pad_o( sda_pad_o ), 
        .sda_padoen_o (sda_padoen_o  ) 
        );
	// User logic ends
封装好以后就是现在的样子。


我们看到非常简介明了。这里没有引出中断,如果要引出并使用的画,要在I2C MASTER里面读明白中断是否保持,从而选择是边缘触发还是电平触发。我不是很倾向于使用中断,因为会将整个系统搞得不好预测,尤其是中断的丢失有时候危害是致命的。有些场合必须中断比如使用LWIP协议,就需要有中断来处理数据,如果用户再加上一个或多个中断,如果添加进来的中断优先级高,可能造成LWIP中断地址,如果添加进来的优先级低,可能造成用户优先级降低。另外中断一般很难在线调试,很多问题在实验没有出现,到用户现场就出现了。所以不是偶发时间或者是用轮询能及时处理的,不建议使用中断。(中断说得有点多哈)。



这样IP核做好了,下步就是调用这个IP并编写C程序驱动起来这个控制器。








最近一个项目需要做I2C的slave,在opencores.org上面找到了一个I2C的代码,不过是master的。 下载来看看,发现里面有一个I2C slave的行为级代码。 于是自己根据这个代码改写了一个I2C slave RTL的代码,并修改了原来那个设计的testbench,将rtl的Slave替换了原来的behavior的Slave,在modelsim里面作了前仿,完全通过。还有一个myram.v文件,是一个register file,和slave相连,存储数据用的。 用synplify做综合,使用x3s400-4的器件,占用LUT<100,速度接近200MHz。性能比较优化。 代码做了详尽的注释,语言采用verilog,并且写了仿真的脚本。解压了直接运行simbehav.bat就可以了。如果modelsim安装的时候注册了环境变量(path),脚本调用modelsim,输入run -all即可看到仿真结果。 虽然不是很复杂,不过对于广大需要做I2C的RTL slave的工程师来说,还是很有参考价值的。 1、 设计流程 将I2C slave的行为模型改为rtl模型。 进行等效仿真,直到波形一致,通过timing check,数据正确。 再进行rtl优化设计 2、 注意要点 a) 时钟的设计 b) 对于restart condition的时序是否正确 c) 3、 进度 a) 11-12:initial状态的bitcnt不对,需要认真比对/设计 b) 11-13:initial基本解决(sda_in的问题)。Sda三态冲突,原因不明。比对原设计 c) 11-14:sda三态冲突解决,原因为sda在初始化时没有将sda_oen赋值(由sm赋值,但是sm没有做async reset)。同时注意verilog的大小写敏感。 d) 11-15:仿真出现错误:read出来的数据非期望值。写入逻辑完全正确。Read时由于sda_oe在sm中有一个cycle_pulse的延迟,导致了mem_do[7]串行移出时错位。在更改了sm的代码风格后再研究解决方法。 e) 11-16:仿真完全匹配波形。计划:优化结构,提高稳定性sm改为每个时钟打一下。 关于I2C的SDA三态换: Master在发送完第8个bit后随后将sda释放(posedge后大概1/4 scl周期),此时slave需要在第九个bit对应的scl的posedge拉低sda。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值