AXI IIC wrapper module within Overlay shows up as DocumentedHierarchy

PYNQ version: 3.0.1, Board name: PYNQ-Z1, Tool version: Vivado 2022.2 .

I am trying to build a simple hardware overlay to capture data from an I2C slave device that I have using the PYNQ-Z1. For this I will need the AXI IIC module. However, it seems like this module assumes that the user implements a tri-state buffer to make the the SDA and SCL lines bidirectional.

So to do this, I’ve created a wrapper module in verilog for the AXI IIC IP, so that I can include the tri-state buffers, and then add the wrapper module into the block design that will become the hardware overlay. This is the wrapper module:

module axi_iic_wrapper(
    input s_axi_aclk, s_axi_aresetn, s_axi_awvalid, s_axi_arvalid, s_axi_wvalid, s_axi_bready, s_axi_rready,
    [8 : 0] s_axi_awaddr, s_axi_araddr,
    [31 : 0] s_axi_wdata,
    [3 : 0] s_axi_wstrb,
    inout sda, scl,
    output s_axi_awready, s_axi_arready, s_axi_wready, s_axi_bvalid, s_axi_rvalid,
    [1 : 0] s_axi_bresp, s_axi_rresp,
    [31 : 0] s_axi_rdata
    );
    
    axi_iic_0 axi_iic (
        .s_axi_aclk(s_axi_aclk),        // input wire s_axi_aclk
        .s_axi_aresetn(s_axi_aresetn),  // input wire s_axi_aresetn
        .iic2intc_irpt(iic2intc_irpt),  // output wire iic2intc_irpt
        .s_axi_awaddr(s_axi_awaddr),    // input wire [8 : 0] s_axi_awaddr
        .s_axi_awvalid(s_axi_awvalid),  // input wire s_axi_awvalid
        .s_axi_awready(s_axi_awready),  // output wire s_axi_awready
        .s_axi_wdata(s_axi_wdata),      // input wire [31 : 0] s_axi_wdata
        .s_axi_wstrb(s_axi_wstrb),      // input wire [3 : 0] s_axi_wstrb
        .s_axi_wvalid(s_axi_wvalid),    // input wire s_axi_wvalid
        .s_axi_wready(s_axi_wready),    // output wire s_axi_wready
        .s_axi_bresp(s_axi_bresp),      // output wire [1 : 0] s_axi_bresp
        .s_axi_bvalid(s_axi_bvalid),    // output wire s_axi_bvalid
        .s_axi_bready(s_axi_bready),    // input wire s_axi_bready
        .s_axi_araddr(s_axi_araddr),    // input wire [8 : 0] s_axi_araddr
        .s_axi_arvalid(s_axi_arvalid),  // input wire s_axi_arvalid
        .s_axi_arready(s_axi_arready),  // output wire s_axi_arready
        .s_axi_rdata(s_axi_rdata),      // output wire [31 : 0] s_axi_rdata
        .s_axi_rresp(s_axi_rresp),      // output wire [1 : 0] s_axi_rresp
        .s_axi_rvalid(s_axi_rvalid),    // output wire s_axi_rvalid
        .s_axi_rready(s_axi_rready),    // input wire s_axi_rready
        .sda_i(sda_i),                  // input wire sda_i
        .sda_o(sda_o),                  // output wire sda_o
        .sda_t(sda_t),                  // output wire sda_t
        .scl_i(scl_i),                  // input wire scl_i
        .scl_o(scl_o),                  // output wire scl_o
        .scl_t(scl_t),                  // output wire scl_t
        .gpo(gpo)                      // output wire [0 : 0] gpo  
);

    assign scl = (scl_t) ?  scl_o : 1'bZ;
    assign scl_i = scl;
    
    assign sda = (sda_t) ?  sda_o : 1'bZ;   
    assign sda_i = sda;                     
    
endmodule

This is the block design for the overlay:

To be able to communicate with the IIC module from the python interface, I will need to use this driver class: pynq.lib.iic Module — Python productivity for Zynq (Pynq)

However, when I look into my hardware overlay from python, there doesn’t seem to be a way for me to get access to the IIC module in a form where I can bind it to an AxiIIC object, because it doesn’t show up in the ip_dict. Instead, the wrapper module shows up as a DocumentedHierarchy object which can be seen from the overview description of the module shown below:

Type:           DocumentedHierarchy
String form:    <pynq.overlay.DocumentHierarchy.<locals>.DocumentedHierarchy object at 0xac79a760>
File:           /usr/local/share/pynq-venv/lib/python3.10/site-packages/pynq/overlay.py
Docstring:     
Default documentation for hierarchy axi_iic_wrapper_0. The following
attributes are available on this hierarchy:

IP Blocks
----------
None

Hierarchies
-----------
None

Interrupts
----------
None

GPIO Outputs
------------
None

Memories
------------
None
Init docstring: Create a new _IPMap based on a hierarchical description.

I’ve also attached the .bit file that I built and included into the SD image. So the main question is: How can I bind the AXI IIC IP to a pynq.lib.iic.AxiIIC object so that I can interact with it? Or if that isn’t the correct way of doing things, how can I write a driver to interact with the AXI IIC IP after I’ve included the design/overlay files into the Z1 board? Appreciate all the help and support on this.
PYNQ_overlay_wrapper.bit (3.9 MB)

Hi @hsfalsharif,

I’m having a similar problem with hierarchies masking IP in RTL wrappers. I have not tested this much at all so this total hack may have other consequences but at least for my simple test case this seems to have done the trick:

assuming your overlay is in variable overlay and your IP has name ip_name:

1.) check with overlay? and see if your RTL is listed as both an IP and a hierarchy, if that is the case I think this will work.

2.) Once the overlay is initialized call overlay._ip_map._description['hierarchies'].pop(ip_name)

3.) Next, call overlay._ip_map.__getattr__(ip_name)

After this you should be able to call
overlay.ip_name to access the IP driver, or at least it did for me.

Not sure why the parser is finding wrapped RTL as a hierarchy and IP but I don’t have the time/inclination to go hacking through that to figure it out. Hopefully this helps (no promises)!

1 Like