基于FPGA的IIC多通道选择器(IIC Switch/Bridge)
基于FPGA的IIC多通道选择器(IIC Switch/Bridge)
- 1,背景
- 2,系统框图
- 3,完整模块代码
1,背景
在项目中,一个FPGA芯片上接了一路IIC Master,至少一路IIC Slave,当IIC Master想要直接访问IIC slave的时候,在FPGA中并不能直接将SCL和SDA端口直接连接,原因是SDA是inout的端口(SCL由主机提供,可以作为单独的input或output),不支持直接相连,否则编译器会报错。为解决这个小尴尬,简单写了一个verilog的IIC switch模块,在此记录一下。使用该模块可以轻松将IIC链路通过FPGA连接,实现通信。
2,系统框图
使用 iic_switch.v 模块,可在FPGA中选择MCU的IIC接到设备端的那一路IIC端口。
3,完整模块代码
`timescale 1ns / 1ps
//
// Company:
// Engineer: QSJ
//
// Create Date: 2025/05/21 9:12:00
// Design Name:
// Module Name: iic_switch
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//module iic_switch(input clk // 100MHz,input rst_n ,input wire [3:0] iv_channel_sel,input i_master_scl ,inout io_master_sda ,output reg o_slave_0_scl ,inout io_slave_0_sda ,output reg o_slave_1_scl ,inout io_slave_1_sda ,output reg o_slave_2_scl ,inout io_slave_2_sda ,output reg o_slave_3_scl ,inout io_slave_3_sda ,output reg o_slave_4_scl ,inout io_slave_4_sda ,output reg o_slave_5_scl ,inout io_slave_5_sda ,output reg o_slave_6_scl ,inout io_slave_6_sda ,output reg o_slave_7_scl ,inout io_slave_7_sda
);reg master_scl_n;
reg master_sda_n;
reg scl_rising ;
reg scl_falling ;
reg sda_rsiing ;
reg sda_falling ;always @(posedge clk)beginif(!rst_n)beginmaster_scl_n <= 1'b1;master_sda_n <= 1'b1;end else beginmaster_scl_n <= i_master_scl;master_sda_n <= io_master_sda;scl_rising <= (master_scl_n!=i_master_scl) & (master_scl_n==0);scl_falling <= (master_scl_n!=i_master_scl) & (master_scl_n==1); sda_rsiing <= (master_sda_n!=io_master_sda) & (master_sda_n==0); sda_falling <= (master_sda_n!=io_master_sda) & (master_sda_n==1); end
endreg [7:0] bit_cnt ;
reg first_start ;
reg iic_start ;
reg iic_stop ;always @(posedge clk)beginif(!rst_n)beginbit_cnt <= 'd0;first_start <= 'b0;iic_start <= 'b0;iic_stop <= 'b0;end else beginif((i_master_scl==1) & sda_falling)begin //IIC Startbit_cnt <= 0;first_start <= 'b1;iic_start <= 'b1;iic_stop <= 'b0;end else if((i_master_scl==1) & sda_rsiing)begin //IIC Stopbit_cnt <= 0;first_start <= 'b0;iic_start <= 'b0;iic_stop <= 'b1;end else if(bit_cnt==10)beginbit_cnt <= 1;first_start <= 'b0;iic_start <= 'b0;iic_stop <= 'b0;end else beginbit_cnt <= bit_cnt + scl_falling;first_start <= first_start;iic_start <= 'b0;iic_stop <= 'b0;end end
endreg rw_bit;
reg ack_bit_vld;
reg ack_bit_vld_n;
wire ack_bit_vld_falling = (ack_bit_vld_n!=ack_bit_vld) & (ack_bit_vld_n==1);
always @(posedge clk)beginif(!rst_n)beginack_bit_vld <= 1'b0;ack_bit_vld_n <= 1'b0;end else beginack_bit_vld <= bit_cnt==9;ack_bit_vld_n <= ack_bit_vld;end
endreg rw_bit_p;
reg master_ack;
always @(posedge clk)beginif(!rst_n)beginmaster_ack <= 1'b0;end else beginif(rw_bit_p)beginif(ack_bit_vld_n & scl_rising)beginmaster_ack <= master_sda_n;end else beginmaster_ack <= master_ack;end end else beginmaster_ack <= 0;end end
endreg bit_vld;
always @(posedge clk)beginif(!rst_n)beginbit_vld <= 'b0;rw_bit <= 'b0;rw_bit_p <= 'b0;end else beginbit_vld <= scl_rising;rw_bit_p <= ((bit_cnt==8) & bit_vld & first_start) ? io_master_sda : iic_stop ? 1'b0 : rw_bit_p;rw_bit <= ack_bit_vld_falling ? master_ack ? 1'b0 : rw_bit_p : rw_bit;endendreg master_sda_oe;
always @(posedge clk)beginif(!rst_n)beginmaster_sda_oe <= 1'b0;end else beginmaster_sda_oe <= rw_bit ? ~ack_bit_vld_n : ack_bit_vld_n;end
end
wire slave_sda_oe = ~master_sda_oe;assign io_slave_0_sda = iv_channel_sel==0 ? slave_sda_oe ? io_master_sda : 1'bz : 1'bz;
assign io_slave_1_sda = iv_channel_sel==1 ? slave_sda_oe ? io_master_sda : 1'bz : 1'bz;
assign io_slave_2_sda = iv_channel_sel==2 ? slave_sda_oe ? io_master_sda : 1'bz : 1'bz;
assign io_slave_3_sda = iv_channel_sel==3 ? slave_sda_oe ? io_master_sda : 1'bz : 1'bz;
assign io_slave_4_sda = iv_channel_sel==4 ? slave_sda_oe ? io_master_sda : 1'bz : 1'bz;
assign io_slave_5_sda = iv_channel_sel==5 ? slave_sda_oe ? io_master_sda : 1'bz : 1'bz;
assign io_slave_6_sda = iv_channel_sel==6 ? slave_sda_oe ? io_master_sda : 1'bz : 1'bz;
assign io_slave_7_sda = iv_channel_sel==7 ? slave_sda_oe ? io_master_sda : 1'bz : 1'bz;assign io_master_sda = master_sda_oe ? iv_channel_sel==0 ? io_slave_0_sda : iv_channel_sel==1 ? io_slave_1_sda : iv_channel_sel==2 ? io_slave_2_sda : iv_channel_sel==3 ? io_slave_3_sda : iv_channel_sel==4 ? io_slave_4_sda : iv_channel_sel==5 ? io_slave_5_sda : iv_channel_sel==6 ? io_slave_6_sda : iv_channel_sel==7 ? io_slave_7_sda : 1'bz: 1'bz;always @(*)begincase(iv_channel_sel)0:begino_slave_0_scl = i_master_scl;o_slave_1_scl = 1'bz;o_slave_2_scl = 1'bz;o_slave_3_scl = 1'bz;o_slave_4_scl = 1'bz;o_slave_5_scl = 1'bz;o_slave_6_scl = 1'bz;o_slave_7_scl = 1'bz;end1:begino_slave_0_scl = 1'bz;o_slave_1_scl = i_master_scl;o_slave_2_scl = 1'bz;o_slave_3_scl = 1'bz;o_slave_4_scl = 1'bz;o_slave_5_scl = 1'bz;o_slave_6_scl = 1'bz;o_slave_7_scl = 1'bz;end2:begino_slave_0_scl = 1'bz;o_slave_1_scl = 1'bz;o_slave_2_scl = i_master_scl;o_slave_3_scl = 1'bz;o_slave_4_scl = 1'bz;o_slave_5_scl = 1'bz;o_slave_6_scl = 1'bz;o_slave_7_scl = 1'bz;end3:begino_slave_0_scl = 1'bz;o_slave_1_scl = 1'bz;o_slave_2_scl = 1'bz;o_slave_3_scl = i_master_scl;o_slave_4_scl = 1'bz;o_slave_5_scl = 1'bz;o_slave_6_scl = 1'bz;o_slave_7_scl = 1'bz;end4:begino_slave_0_scl = 1'bz;o_slave_1_scl = 1'bz;o_slave_2_scl = 1'bz;o_slave_3_scl = 1'bz;o_slave_4_scl = i_master_scl;o_slave_5_scl = 1'bz;o_slave_6_scl = 1'bz;o_slave_7_scl = 1'bz;end5:begino_slave_0_scl = 1'bz;o_slave_1_scl = 1'bz;o_slave_2_scl = 1'bz;o_slave_3_scl = 1'bz;o_slave_4_scl = 1'bz;o_slave_5_scl = i_master_scl;o_slave_6_scl = 1'bz;o_slave_7_scl = 1'bz;end6:begino_slave_0_scl = 1'bz;o_slave_1_scl = 1'bz;o_slave_2_scl = 1'bz;o_slave_3_scl = 1'bz;o_slave_4_scl = 1'bz;o_slave_5_scl = 1'bz;o_slave_6_scl = i_master_scl;o_slave_7_scl = 1'bz;end7:begino_slave_0_scl = 1'bz;o_slave_1_scl = 1'bz;o_slave_2_scl = 1'bz;o_slave_3_scl = 1'bz;o_slave_4_scl = 1'bz;o_slave_5_scl = 1'bz;o_slave_6_scl = 1'bz;o_slave_7_scl = i_master_scl;enddefault:begin o_slave_0_scl = i_master_scl;o_slave_1_scl = 1'bz;o_slave_2_scl = 1'bz;o_slave_3_scl = 1'bz;o_slave_4_scl = 1'bz;o_slave_5_scl = 1'bz;o_slave_6_scl = 1'bz;o_slave_7_scl = 1'bz; endendcaseendendmodule