Jump to content


  • Posts

  • Joined

  • Last visited

mea1201's Achievements

Advanced Member

Advanced Member (2/2)



  1. my_custom_t could be any user-defined type, such as typedef struct packed { logic parity; logic [7:0] data; } my_custom_t; I realized that I had a problem with the abstract class, which was originally defined as virtual class my_abstract#(type T=byte, string Tname=""); pure virtual task transact(input my_trans#(T, Tname) trans); endclass : my_abstract I fixed the concrete class to class my_concrete extends my_abstract#(T, Tname); But, the call to transact from the monitor, which has a handle to my_bfm via the configuration database, still causes the error where the formal is claimed to be parameterized with the defaults in the abstract class. I ended up working around this by redefining the abstract class to be non-parameterized, and redeclared the method arguments with non-parameterized base classes (uvm_sequence_item instead of my_trans#(T, Tname)). This got through VCS elaboration. However, it was painful and time-consuming debugging this kind of problem dealing with incompatible types.
  2. I have a method inside a concrete class defined in a module to be instantiated in the testbench. The concrete class handle is passed to the UVM agent. I chose this approach over virtual interfaces to minimize the proliferation of parameters, because the interface by definition includes a decent set of parameters. I do need to support a type parameter for modeling the data in any way useful for the verification environment, and a corresponding string parameter that is the type's name for UVM factory reasons that I won't delve into. Anyway, here is example code to illustrate what I'm trying to describe: module my_bfm#( parameter type T = byte, parameter string Tname = "", parameter BLAH = DEFAULT_BLAH ); class my_concrete extends my_abstract; task transact(input my_trans#(T, Tname) trans); // Do something... endtask : transact endclass : my_concrete endmodule : my_bfm module testbench; my_bfm#(.T(my_custom_t), .Tname("my_custom_t"), .BLAH(42)); endmodule : testbench class my_trans#(type T=byte, string Tname="") extends uvm_sequence_item; `uvm_object_param_utils(my_trans#(T)) endclass : my_trans VCS will fail at elaboration with the following: Error-[ICTTFC] Incompatible complex type usage my_monitor.svh, 42 Incompatible complex type usage in task or function call. The following expression is incompatible with the formal parameter of the task. The type of the actual is 'class my_trans#(my_custom_t,"my_custom_t")', while the type of the formal is 'class my_trans#(byte,"\000")'. Expression: this.trans Source info: my_abstract::collect_transaction (this.m_bfm, this.trans); Questa passes compilation and elaboration. It seems that VCS is not propagating the parameter overrides in the testbench to the concrete class where the method using similarly parameterized classes is defined. The UVM classes are overridden from the env with the same type/value as in the testbench. Anyone have any ideas, perhaps someone from Synopsys?
  3. If you're talking about different encapsulations, then you can go about it in one of two possible ways: Define a single packet model that is flat, and includes knobs (random and non-random) to determine if a particular header is present or not. Define a class hierarchy/family of packet types. I think #1 is simpler and less unwieldy to deal with. Does that sort of address your question?
  4. Thanks, Dave. Actually, the with range needed to match the declared range of bit_vector: bit_array = {<<{bit_vector with [packer.count-1:0]}}; The fatal was resolved; I overlooked something, and it had nothing to do with the streaming assignment after all.
  5. No need. You can have your sequencer send down the packet streams in parallel threads, and define your sequence arbitration scheme however you wish (or leave it default if that's good enough). You could also set up a single stream that can mix up the packet types using random constraints or whatever. Technically, you could add more TLM connections between the sequencer and driver, but that's probably unnecessary. Besides, the driver can only push one packet at a time onto your interface, so the infrastructure that already exists between the sequencer and driver works quite well in a lot of cases.
  6. I'm stuck on a problem I encountered with a use of the streaming operator to unpack into a dynamic array. Rather than write procedural code, I thought I could use it actually with some amount of success. Here is what the code more or less looks like: uvm_pack_bitstream_t bit_vector; bit bit_array[]; ... bit_vector = packer.get_packed_bits(); {<<{bit_array with [0 +: packer.count]}} = bit_vector; myfunc(bit_array); Yes, I'm trying to use "unadvertised" features of the UVM packer here. This has worked in many cases, but there is one case that led to the following: # ** Fatal: Width do not match. Stream larger than variable. I thought maybe the packer count was too large compared to the bits returned from the packer, but similar scenarios had executed previously in the simulation without error. BTW, this is the only thing reported in the error, which is followed by the file and line number where it occurred (i.e. the streaming operator above). So, any ideas or insight as to what is going on here? TIA
  7. I think one way you can do what you're trying to do is to set up a UVM heartbeat that watches for activity. There is a very good paper written on this. I think you can find it in the contributions area. Basically, you need logic that raises an objection to allow the simulation to run further, and drop it when you detect enough inactivity during a given window (a timeout mechanism). That's the basic idea that I think is what you might be looking for.
  8. Can't you detect SOF and EOF from the serial input that you're monitoring? Wouldn't that be a more reliable way to collect your packets instead of knowing the packet length ahead of time, and drive a fixed loop with that? How can you know the packet length before you even start collecting the packet in the monitor? I don't get it. I would also add the pkt_len variable to the transaction class, and let do_unpack determine the length and save it to the variable in the transaction. That way, the length is always coupled with the packet it belongs to. Anyone else interested in a packet's length can do a simple query or get to obtain the value. I'm not sure it really makes much sense to store SOF and EOF in the transaction. I would imagine that whatever your serial bitstream is that the SOF and EOF are encoded, so you can set up a state machine in your monitor to detect the packet boundaries. Otherwise, I just don't understand how you can realistically determine where packets begin and end from a random stream of bits. Does any of this make sense? Or, have I completely misunderstood what you are trying to do?
  9. Have you checked the UVM class reference? The function prototype for unpack_bytes is function int unpack_bytes(ref byte unsigned bytestream[], input uvm_packer packer = null); You declared dataout (the actual) as an array of 10-bit values, which does not match in type with the bytestream argument (the formal), which is typed as an array of 8-bit values in the prototype. You need to pass in an array of bytes. You'll probably have to design your own 10b8b conversion in your decoder before unpacking into your packet.
  10. Assuming the driver and monitor share the same parent (agent), which is conventional, then you can access any public (i.e. not local, not protected) variables through the parent handle: Agent: my_driver m_drv; my_monitor m_mon; Driver: int unsigned m_pkt_len; Monitor: my_agent m_parent_handle; int unsigned m_pkt_len; // Not the same as m_pkt_len in the driver. ... task run_phase(uvm_phase phase); $cast(m_parent_handle, get_parent()); // Capture the handle of the parent, which should be the agent. m_pkt_len = m_parent_handle.m_drv.m_pkt_len; // Copy packet length from the driver somewhere in this task. // Do what you want. endtask : run_phase However, you should not couple your monitor to your driver in the manner you described. What if your agent is configured to passive mode and is responsible only for monitoring, which could be the case, for example, if you integrate the agent up into a system-level env from a block-level env, and the active agent would have to be switched to passive to avoid contention with design blocks in the system level. If you're doing what you're doing as a quick-and-dirty debugging thing, then fine. But, just be very careful. The monitor should never know anything about what the driver is doing, usually. If you need some sort of driver/monitor interaction aside from the physical interface, and something more permanent, then I think the better solution is to connect the two in the agent via TLM ports/exports and FIFOs. You could define a transaction that contains fields for information of interest between the driver and monitor, and pass those objects through the TLM connection. Or, just pass the transaction itself across the connection. For example, flow control using control frames would be one example where you might consider connecting between the agent subcomponents using TLM (driver needs info from the frame collected at the monitor to determine if it should drive or stall).
  11. In do_unpack, you can return the total packet size by querying the packer, then calculate the payload size by subtracting the known sizes of the other fields: virtual function void do_unpack(uvm_packer packer); int unsigned packet_size_in_bytes; super.do_unpack(packer); packet_size_in_bytes = packer.get_packed_size() / $bits(byte); payload_size_in_bytes = packet_size_in_bytes - ( ($bits(sync) + $bits(sof) + $bits(header) + $bits(crc) + $bits(eof)) / $bits(byte) ); // -- Allocate the payload array. // -- Unpack everything here. endfunction : do_unpack Does this help any? Is this more or less what you are looking for?
  12. In general, the better way is to use analysis FIFOs, or instantiate multiple subscribers, rather than the imp_decl macros.
  13. Does VCS support UVM transaction recording? How can I do that? Latest I have available is 2011.12, and I'm supposedly using a UVM 1.1 implementation provided by Synopsys. I found some blurb about $vcdplusmsglog to record transaction streams. Don't tell me that I have to sprinkle that code into my UVM code base alongside the begin_tr/end_tr stuff. The UVM I have is intended to run on multiple simulators. BTW, Questa and Incisive have UVM transaction recording with next to no additional effort to enable.
  14. I agree. However, the issue described is related to type parameters, not value parameters. There are more limitations to overriding types, and none of the major simulators, as far as I know, supports overriding types from the command-line, such as a -g option (for generic) or from a params file. I suppose I could encapsulate the data item with a UVM object, develop a bunch of various classes for that, and use the factory override, which can be dictated by the test class. But, what if I want to keep it lightweight, and be able to override the data item within the transaction with a variety of packed representations. For example, this is representative of the sequence item: class trans #(type T = bit[7:0]) extends uvm_sequence_item; // default type unsigned byte `uvm_object_param_utils(trans#(T)) ... T data; The interface would have tasks that can operate on trans.data, so it is also parameterized the same way. And, the testbench would instantiate the interface such as typedef struct packed { bit[31:0] my_data, bit error_bit } a_data_item_t; // overriding default just to illustrate the point bus_if#(.T(a_data_item_t)) u_bus_if (.*); ... Except, I'd like more variability with the type definition, ideally letting the chosen test determine that. PS: Why is BB editing off???
  15. What's the best way to define the actual data type from a test, and set the module side accordingly? In the class side, the sequence item has a data member. Its type is parameterized, and it's intended to be overridden usually with some sort of bit vector of any size (e.g. bit[N-1:0], packed struct). The rest of the classes are parameterized on this data type. Similarly, the module side is parameterized (interface, top-module). If I define a particular type in a test class, then how can that pass over to the module side? Thanks for any ideas and opinions.
  • Create New...