ssmith Posted September 10, 2014 Report Share Posted September 10, 2014 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 Quote Link to comment Share on other sites More sharing options...
tudor.timi Posted September 10, 2014 Report Share Posted September 10, 2014 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 Quote Link to comment Share on other sites More sharing options...
ssmith Posted September 11, 2014 Author Report Share Posted September 11, 2014 Thanks Tudor - I think your approach will work for contiguous address-spaces, but if I needed to have a subset of addresses, that are not contiguous - how could I best access them from within the agents? Quote Link to comment Share on other sites More sharing options...
David Black Posted September 11, 2014 Report Share Posted September 11, 2014 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. tudor.timi 1 Quote Link to comment Share on other sites More sharing options...
ssmith Posted September 12, 2014 Author Report Share Posted September 12, 2014 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? Quote Link to comment Share on other sites More sharing options...
tudor.timi Posted September 12, 2014 Report Share Posted September 12, 2014 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. Quote Link to comment Share on other sites More sharing options...
ksiddav Posted October 2, 2014 Report Share Posted October 2, 2014 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 ksiddav 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.