verilog 如何做到带双向IO口模块的接口拓展

之前在做一个项目的时候需要使用FPGA与32个I2C设备进行通信,但是一个I2C模块所占的资源也不算太小,如果直接例化32个I2C模块的话将造成FPGA资源的极大浪费。

由于这32个I2C设备不需要同时进行通信,所以我们可以只用一个I2C模块将其接口拓展成32即可。

首先,我们拓展SCL端口。I2C模块的SCL输出信号接口命名为scl_out,以拓展5个接口(scl_0, scl_1, scl_2, scl_3, scl_4, sda_0, sda_1, sda_2, sda_3, sda_4)为例。定义一个选择信号,由于只需要拓展5个,所以3bit数据位就足够了。

reg [2:0] i2c_io_sel;

assign scl_0 = (i2c_io_sel == 3'b000) ? scl_r : 1'b1;
assign scl_1 = (i2c_io_sel == 3'b001) ? scl_r : 1'b1;
assign scl_2 = (i2c_io_sel == 3'b010) ? scl_r : 1'b1;
assign scl_3 = (i2c_io_sel == 3'b011) ? scl_r : 1'b1;
assign scl_4 = (i2c_io_sel == 3'b100) ? scl_r : 1'b1;

如以上代码所示,使用“?:”语句判断是否选择该接口即可。

但是SDA接口是双向接口,需要具有输入和输出功能,定义模块的输入寄存器为 sda_in,于是在信号输入的时候我们可以使用和上面SCL信号拓展的方法,选择到底是哪一路信号输入到模块中。

assign sda_in = (i2c_io_sel == 3'b000) ? sda_0 : 1'bz;
assign sda_in = (i2c_io_sel == 3'b001) ? sda_1 : 1'bz;
assign sda_in = (i2c_io_sel == 3'b010) ? sda_2 : 1'bz;
assign sda_in = (i2c_io_sel == 3'b011) ? sda_3 : 1'bz;
assign sda_in = (i2c_io_sel == 3'b100) ? sda_4 : 1'bz;

至于为什么没有选中的接口要置为高阻态,可以去查一下FPGA怎样使用双向IO口。

OK, SDA信号的输入搞定了,但是它的输出怎么办?也采用刚才的那种方法?

对头!不过还得加一些东西,首先我们需要定义一个SDA方向的标志为 sda_dir,并规定该标志位位1时是输入,标志位为0时是输出。

assign sda_0 = (sda_dir == 0) ? ((i2c_io_sel == 3'b000) ? sda_out : 1'b1) : 1'bz;
assign sda_1 = (sda_dir == 0) ? ((i2c_io_sel == 3'b001) ? sda_out : 1'b1) : 1'bz;
assign sda_2 = (sda_dir == 0) ? ((i2c_io_sel == 3'b010) ? sda_out : 1'b1) : 1'bz;
assign sda_3 = (sda_dir == 0) ? ((i2c_io_sel == 3'b011) ? sda_out : 1'b1) : 1'bz;
assign sda_4 = (sda_dir == 0) ? ((i2c_io_sel == 3'b100) ? sda_out : 1'b1) : 1'bz;

这里使用了“?:”运算符的嵌套,首先是判断SDA信号是输入还是输出,如果是输入则置为高阻态;如果是输出,则判断到底是那个接口输出。

以上就实现了一个I2C模块的接口拓展,不管拓展成多少个接口,其核心是不变的,增加的资源消耗与直接例化多个I2C模块来比几乎可以忽略不记。

发布于 2020-03-12

发布了6 篇原创文章 · 获赞 1 · 访问量 82

猜你喜欢

转载自blog.csdn.net/qq_32290049/article/details/105051708