alexforencich / xfcp

Extensible FPGA control platform
MIT License
52 stars 20 forks source link

I2C blocking problem #3

Closed lk-davidegironi closed 2 years ago

lk-davidegironi commented 2 years ago

Hello Alex, first of all I would like to thank you for sharing your code, I'm already using your verilog-ethernet library on which I've implemented a Ethernet RAW reader/writer for a device I'm working on, it works like a charm. I'm working on a ZYNQ-7010 board. In my design I've added your fpga_core, RAM works fine, but I've a problem with I2C. Clock is 125Mhz. Whenever I try to query/write/read on I2C module, all the core stops working. No output on the SDA and SCL pins. You can find attached an ILA screenshoot. I think the problem is that the TLAST line stays on. An help to your side will be appreciated, I'm sure I'm missing something.

Below the wire you can find in the screenshot (probe 17 and 18 are SCL and SDA)

assign o_in_tvalid = xfcp_switch_port_3_down_tvalid;
assign o_in_tready = xfcp_switch_port_3_down_tready;
assign o_in_tdata = xfcp_switch_port_3_down_tdata;
assign o_in_tlast = xfcp_switch_port_3_down_tlast;
assign o_out_tvalid = xfcp_switch_port_3_up_tvalid;
assign o_out_tready = xfcp_switch_port_3_up_tready;
assign o_out_tdata = xfcp_switch_port_3_up_tdata;
assign o_out_tlast = xfcp_switch_port_3_up_tlast;

assign o_scl = i2c_scl_t ? 1'bz : i2c_scl_o;
assign o_sda = i2c_sda_t ? 1'bz : i2c_sda_o;

If I'll be able to make all the things works, I'll translate the python library to C#, cause main project uses this language. If you are interested I can post it. I've other few questions but I don't want to bother you, so I'll try to solve this by myself before asking :) I'm pretty new to FPGA world, so excuse me if questions may sounds dumb.

Thanks

ila_capture_i2c_01 !

alexforencich commented 2 years ago

I2C is open-drain, somewhat slow, and slave devices are allowed to hold the clock low (clock stretching), so the core will wait until it sees the expected levels on the I2C interface. But the idle state of I2C is high, since nothing is pulling it down. So, why are probe17/probe18 reading low?

lk-davidegironi commented 2 years ago

Thanks for your reply!

I don't know why probe17/probe18 are low. In the snapshot above probe17/probe18 are connected straight to output from fpga_core, not external port involved.

I've made further tests, enabled the full tristate, but again nothing out from SCL or SDA. I can not attach inout ports using System ILA, so I've to assign it to output port.

In the snapshot below probe20/probe21 are connected to monitor_i2c_scl/monitor_i2c_sda and i2c_scl/i2c_sda to i2c_0_scl_io/i2c_0_sda_io constraints

The main fpga_core I2C zone looks like this

           inout  wire       i2c_scl,
           inout  wire       i2c_sda,
           output wire       monitor_i2c_scl,
           output wire       monitor_i2c_sda,

A few lines below in the core.

wire i2c_scl_i;
wire i2c_scl_o;
wire i2c_scl_t;
wire i2c_sda_i;
wire i2c_sda_o;
wire i2c_sda_t;

assign i2c_scl_i = i2c_scl;
assign i2c_scl = i2c_scl_t ? 1'bz : i2c_scl_o;
assign i2c_sda_i = i2c_sda;
assign i2c_sda = i2c_sda_t ? 1'bz : i2c_sda_o;

assign monitor_i2c_scl = i2c_scl;
assign monitor_i2c_sda = i2c_sda;

xfcp_mod_i2c_master #(
    .XFCP_ID_STR("XFCP I2C Master"),
    .DEFAULT_PRESCALE(125000000/(400000*4))
)
i2c_master_inst (
    .clk(clk),
    .rst(rst),
    .up_xfcp_in_tdata(xfcp_switch_port_3_down_tdata),
    .up_xfcp_in_tvalid(xfcp_switch_port_3_down_tvalid),
    .up_xfcp_in_tready(xfcp_switch_port_3_down_tready),
    .up_xfcp_in_tlast(xfcp_switch_port_3_down_tlast),
    .up_xfcp_in_tuser(xfcp_switch_port_3_down_tuser),
    .up_xfcp_out_tdata(xfcp_switch_port_3_up_tdata),
    .up_xfcp_out_tvalid(xfcp_switch_port_3_up_tvalid),
    .up_xfcp_out_tready(xfcp_switch_port_3_up_tready),
    .up_xfcp_out_tlast(xfcp_switch_port_3_up_tlast),
    .up_xfcp_out_tuser(xfcp_switch_port_3_up_tuser),
    .i2c_scl_i(i2c_scl_i),
    .i2c_scl_o(i2c_scl_o),
    .i2c_scl_t(i2c_scl_t),
    .i2c_sda_i(i2c_sda_i),
    .i2c_sda_o(i2c_sda_o),
    .i2c_sda_t(i2c_sda_t)
);

And constrainst

set_property IOSTANDARD LVCMOS18 [get_ports i2c_0_scl_io]
set_property IOSTANDARD LVCMOS18 [get_ports i2c_0_sda_io]
set_property PACKAGE_PIN B20 [get_ports i2c_0_scl_io]
set_property PACKAGE_PIN C20 [get_ports i2c_0_sda_io]
set_property PULLUP true [get_ports i2c_0_scl_io]
set_property PULLUP true [get_ports i2c_0_sda_io]

Hardware side there's a 24C256 connected to B20 and C20.

Vivado version is 2021, xfcp is at latest version

ila_capture_i2c_02 2

alexforencich commented 2 years ago

I recommend probing the _i, _o, and _t signals. Anyway, I suspect a board - level issue. Did you forget the pull - up resistors? Or maybe the pin constraints are incorrect?

lk-davidegironi commented 2 years ago

Really sorry to bother you, I do have a blog and release opensource too - although i'm not as expertise as you :) -, I know helps other people takes time and often it's for "dumb" things.

PIN B20 and C20 are ok if tested with blinking IP (tested on scope). 24C256 is tested ok on micro (STM32) at 3.3V (attached the board I'm using). I've renamed pins and double checked it.

fpga_gpio_0 is attached to SCL with 4.7 pullup of 24C256, operating at 3.3V, fpga_gpio_1 is attached to SDA.

There's for sure a mistake somewhere, I can't find it where. Attached the latest ILA screenshot, with _i, _o, _t monitored like in the code below. For the probe just numbered: probe20 -> monitor_i2c_scl probe21 -> monitor_i2c_sda probe22 -> monitor_i2c_scl_i probe25 -> monitor_i2c_sda_i

set_property IOSTANDARD LVCMOS18 [get_ports fpga_gpio_0]
set_property PACKAGE_PIN C20 [get_ports fpga_gpio_0]
set_property IOSTANDARD LVCMOS18 [get_ports fpga_gpio_1]
set_property PACKAGE_PIN B20 [get_ports fpga_gpio_1]
set_property PULLUP true [get_ports fpga_gpio_0]
set_property PULLUP true [get_ports fpga_gpio_1]

For consistency, code below

...
inout  wire       i2c_scl,
inout  wire       i2c_sda,

output wire       monitor_i2c_scl,
output wire       monitor_i2c_sda,
output wire       monitor_i2c_scl_i,
output wire       monitor_i2c_scl_o,
output wire       monitor_i2c_scl_t,
output wire       monitor_i2c_sda_i,
output wire       monitor_i2c_sda_o,
output wire       monitor_i2c_sda_t,
...

wire i2c_scl_i;
wire i2c_scl_o;
wire i2c_scl_t;
wire i2c_sda_i;
wire i2c_sda_o;
wire i2c_sda_t;

assign i2c_scl_i = i2c_scl;
assign i2c_scl = i2c_scl_t ? 1'bz : i2c_scl_o;
assign i2c_sda_i = i2c_sda;
assign i2c_sda = i2c_sda_t ? 1'bz : i2c_sda_o;

assign monitor_i2c_scl = i2c_scl;
assign monitor_i2c_sda = i2c_sda;
assign monitor_i2c_scl_i = i2c_scl_i;
assign monitor_i2c_scl_o = i2c_scl_o;
assign monitor_i2c_scl_t = i2c_scl_t;
assign monitor_i2c_sda_i = i2c_sda_i;
assign monitor_i2c_sda_o = i2c_sda_o;
assign monitor_i2c_sda_t = i2c_sda_t;

xfcp_mod_i2c_master #(
...
    .i2c_scl_i(i2c_scl_i),
    .i2c_scl_o(i2c_scl_o),
    .i2c_scl_t(i2c_scl_t),
    .i2c_sda_i(i2c_sda_i),
    .i2c_sda_o(i2c_sda_o),
    .i2c_sda_t(i2c_sda_t)
...

My board is the MIYR MYD-Y7Z010 Development Board http://www.myirtech.com/list.asp?id=580

ila_capture_i2c_04 ila_capture_i2c_05 41erMNKzORL _AC_

lk-davidegironi commented 2 years ago

Solved Yesterday I had time to investigate this problem, turn out it was my tristate implementation. It's a newbie problem I know. I'm now using IOBUF.

My wiring for the I2C node below, just in case, for other people.

// XFCP I2C Master
wire i2c_scl_i;
wire i2c_scl_o;
wire i2c_scl_t;
wire i2c_sda_i;
wire i2c_sda_o;
wire i2c_sda_t;

IOBUF sda_iobuf (
  .I(i2c_sda_o),
  .O(i2c_sda_i),
  .T(i2c_sda_t),
  .IO(i2c_sda)
);

IOBUF scl_iobuf (
  .I(i2c_scl_o),
  .O(i2c_scl_i),
  .T(i2c_scl_t),
  .IO(i2c_scl)
);

xfcp_mod_i2c_master #
    (
        .XFCP_ID_STR("XFCP I2C Master"),
        .DEFAULT_PRESCALE(125000000/(100000*4))
    )
    i2c_master_inst (
        .clk(clk),
        .rst(rst),
        .up_xfcp_in_tdata(xfcp_switch_port_2_down_tdata),
        .up_xfcp_in_tvalid(xfcp_switch_port_2_down_tvalid),
        .up_xfcp_in_tready(xfcp_switch_port_2_down_tready),
        .up_xfcp_in_tlast(xfcp_switch_port_2_down_tlast),
        .up_xfcp_in_tuser(xfcp_switch_port_2_down_tuser),
        .up_xfcp_out_tdata(xfcp_switch_port_2_up_tdata),
        .up_xfcp_out_tvalid(xfcp_switch_port_2_up_tvalid),
        .up_xfcp_out_tready(xfcp_switch_port_2_up_tready),
        .up_xfcp_out_tlast(xfcp_switch_port_2_up_tlast),
        .up_xfcp_out_tuser(xfcp_switch_port_2_up_tuser),
        .i2c_scl_i(i2c_scl_i),
        .i2c_scl_o(i2c_scl_o),
        .i2c_scl_t(i2c_scl_t),
        .i2c_sda_i(i2c_sda_i),
        .i2c_sda_o(i2c_sda_o),
        .i2c_sda_t(i2c_sda_t)
    );

My constraints for the wiring (on MIYR MYD-Y7Z010)

inout wire _i2cscl connected to pin _gpio1 inout wire _i2csda connected to pin _gpio0

set_property IOSTANDARD LVCMOS18 [get_ports gpio_0]
set_property PACKAGE_PIN L17 [get_ports gpio_0]
set_property IOSTANDARD LVCMOS18 [get_ports gpio_1]
set_property PACKAGE_PIN L16 [get_ports gpio_1]
set_property PULLUP true [get_ports gpio_0]
set_property PULLUP true [get_ports gpio_1]

To read my 24C256 EEPROM I've to assert a write to point the address to read. So just for test I've changed xfcp_ctrl.py read_i2c function to call _write_readi2c instead of _readi2c