基于FPGA的IIC多通道选择器(IIC Switch/Bridge)
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,完整模块代码
javascript
`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)begin
if(!rst_n)begin
master_scl_n <= 1'b1;
master_sda_n <= 1'b1;
end else begin
master_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
end
reg [7:0] bit_cnt ;
reg first_start ;
reg iic_start ;
reg iic_stop ;
always @(posedge clk)begin
if(!rst_n)begin
bit_cnt <= 'd0;
first_start <= 'b0;
iic_start <= 'b0;
iic_stop <= 'b0;
end else begin
if((i_master_scl==1) & sda_falling)begin //IIC Start
bit_cnt <= 0;
first_start <= 'b1;
iic_start <= 'b1;
iic_stop <= 'b0;
end else if((i_master_scl==1) & sda_rsiing)begin //IIC Stop
bit_cnt <= 0;
first_start <= 'b0;
iic_start <= 'b0;
iic_stop <= 'b1;
end else if(bit_cnt==10)begin
bit_cnt <= 1;
first_start <= 'b0;
iic_start <= 'b0;
iic_stop <= 'b0;
end else begin
bit_cnt <= bit_cnt + scl_falling;
first_start <= first_start;
iic_start <= 'b0;
iic_stop <= 'b0;
end
end
end
reg 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)begin
if(!rst_n)begin
ack_bit_vld <= 1'b0;
ack_bit_vld_n <= 1'b0;
end else begin
ack_bit_vld <= bit_cnt==9;
ack_bit_vld_n <= ack_bit_vld;
end
end
reg rw_bit_p;
reg master_ack;
always @(posedge clk)begin
if(!rst_n)begin
master_ack <= 1'b0;
end else begin
if(rw_bit_p)begin
if(ack_bit_vld_n & scl_rising)begin
master_ack <= master_sda_n;
end else begin
master_ack <= master_ack;
end
end else begin
master_ack <= 0;
end
end
end
reg bit_vld;
always @(posedge clk)begin
if(!rst_n)begin
bit_vld <= 'b0;
rw_bit <= 'b0;
rw_bit_p <= 'b0;
end else begin
bit_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;
end
end
reg master_sda_oe;
always @(posedge clk)begin
if(!rst_n)begin
master_sda_oe <= 1'b0;
end else begin
master_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 @(*)begin
case(iv_channel_sel)
0: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;
end
1:begin
o_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;
end
2:begin
o_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;
end
3:begin
o_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;
end
4:begin
o_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;
end
5:begin
o_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;
end
6:begin
o_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;
end
7:begin
o_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;
end
default: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; end
endcase
end
endmodule