Jump to content

Configure DUT ports from outside agent


Recommended Posts

Hi All,

  I am learning UVM at the moment and I am working on an I2C slave monitor.  I want to put multiple I2C slaves on a bus, each slave will have a dedicated agent.  One of these agents will be active - driving transactions to the across the interface.  Each slave should access the bus by driving SDA at any one time.

 

I wish to put a monitor on the SDA line, such that any time it is driven by a slave (not the master) - a check that the slave was previously addressed can be performed.  Part of this check requires information on pins from the DUT (SDA_out, slave_address[x:y]).  How can I get this information into the monitor of each agent without hard-coding the values?

 

Thanks,

 

S

Link to comment
Share on other sites

Just to make sure we're on the same page, inside one slave agent you need to know the address spaces of the other slave agents, right?

 

To share the information, you could use something like an address map class. Off the top of my head:

class i2c_addr_map;
  // boilerplate ...

  // associative array to hold start/end addresses
  // of each slave
  local int unsigned start_addresses[string];
  local int unsigned end_addresses[string];
  
  // function to add a slave
  function void add_slave(string name, int unsigned start_addr, int unsigned end_addr);
    // ... fill the associative arrays
    // ... check that you didn't already add a slave with the name
  endfunction

  // a utility function to return the slave at a
  // certain address
  function string get_slave_by_address(int unsigned address);
    // ... query the associative arrays
  endfunction
endclass

You can give each slave a unique name and store its start and end addresses. To simplify adding slaves, you can define a function for this. You can also create a utility function to return what slave contains a specific address. This way, everything is nice and encapsulated.

 

Before you create your environment, where you instantiate your agents, you create this address map. You then pass the handle to the environment, which then passes it to the agents:

addr_map = i2c_addr_map::type_id::create("addr_map");
addr_map.add_slave("slave1", 'h000, 'h0ff);
addr_map.add_slave("slave2", 'h100, 'h1ff);
i2c_env.addr_map = addr_map
class i2c_env;
  // ...
  
  i2c_addr_map addr_map;
  
  function void connect_phase(uvm_phase phase);
    // pass addr_map to each agent
  endfunction
endclass
Link to comment
Share on other sites

Replace start and end address range with another associative array. Populate it with the addresses of interest:

class i2c_addr_map;
  ...
  typedef int unsigned addr_t;
  typedef addr_t       list_t[$];

  local list_t slave2addrs[string];
  local string addr2slave[addr_t];

  // Constructor -- uses default

  // function to add a slave
  function void add_slave(string name, int unsigned addr_list[$]);
    assert(name != ""); //< minimal criteria
    if (slave2addrs.exists(name)) `uvm_warning("i2c_addr_map","i2c device name already exists! - adding")
    foreach (addr_list[i]) begin
      if (addr2slave.exists(addr_list[i]) && addr2slave[addr_list[i] != name) begin
        `uvm_error("i2c_addr_map","Overlapping i2c address - ignoring")
      end else begin
        addr2slave[addr_list[i]] = name;
        slave2addrs[name].push_back(addr_list[i]);
      end
    end
  endfunction

  // a utility function to return the slave at a certain address
  function string get_slave_by_address(addr_t address);
    if (addr2slave.exists(address)) return addr2slave[address];
    `uvm_error("i2c_addr_map","no such i2c device name registered")
    return "";
  endfunction
endclass

Now you have to add all the addresses:

addr_map.add_slave("slave1", {'h10,'h11,'h13});

Obviously, you could add methods for adding ranges and make it more complex as you like.

Link to comment
Share on other sites

Thanks again for your time and expertise with this.  The other side of this problem, is as follows.

 

If I have multiple I2C slave-devices, one per agent, connected to the shared I2C bus.  How can I know which device pulled down the SDA line when addressed?  I would like to configure each agent with the actual name of the pin driving SDA, and have the scoreboard access this pin name and check it via the interface (where all pins will be declared for all slave-devices)?

 

Can this be done with the configuration database in some way?

Link to comment
Share on other sites

I've read up a bit on the I2C protocol and don't see why you need a scoreboard to do this check. This you can check in the agent directly.

 

I would set up your interface like this:

interface i2c_if(inout sda, scl);
  // per default in idle state
  logic node_sda = 1;
  logic node_scl = 1;
  
  buf (pull1, strong0) (sda, node_sda);
  buf (pull1, strong0) (scl, node_scl);
endinterface

This way you have the "global" SDA and SCL lines available, but also the values that the "node" (master or slave) wants to drive. You connect all of your SDAs and SCLs between your interfaces and connect the 'node_sda/scl' signals to your DUTs. This way, when a DUT wants to drive a 0 on SDA, you monitor the 'node_sda' signal and check that after it's been addressed that it lowers it. Also, don't forget to check the complementary of this: that a slave doesn't lower SDA when it's not supposed to lower it.

 

Here's a small example I made on EDA playground where the master drives SDA and SCL while the slaves wait. You can extend this to your case: http://www.edaplayground.com/x/T6v

 

Code here in case the link goes down in the future:

module top;
  // the "global" SDA and SCL lines
  wire sda, scl;
  
  i2c_if master(.*);
  i2c_if slave1(.*);
  i2c_if slave2(.*);
  
  initial begin
    // master doing a request
    master.node_sda <= 0;
    master.node_scl <= 0;
    #1;
    master.node_scl <= 1;
    #1;
    
    // the master can drive an address here
    // some slave is supposed to answer here
  end
endmodule

P.S. I'm not an expert in Verilog gate level constructs, so if what I'm saying is complete garbage I hope someone will correct me.

Link to comment
Share on other sites

  • 3 weeks later...

HI,

You are monitoring all slaves’ behavior. So you need to have a monitor for entire I2C system. The monitor should have access to all the slaves' inputs and outputs.

You need to create uvm_pool class to create user data base pool. In monitor, you need to update the SLAVE ID based on who wins the arbitration.

Thanks,

Kartheek R S 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...