Jump to content

mea1201

Members
  • Content Count

    46
  • Joined

  • Last visited

Everything posted by mea1201

  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.
  16. Put the array of analysis FIFOs, as well as an array of analysis exports, inside your scoreboard. In your env, connect the agents to the analysis exports. Within the scoreboard, connect the analysis exports to the analysis FIFOs. In the run phase in the scoreboard, fork off a thread for each analysis FIFO. Each thread calls get method from the associated FIFO, then does something when it receives a transaction. The rest of the scoreboard logic, including coordinating all the threads, is entirely up to you. No need to implement any write method in your scoreboard for each and every master/slave.
  17. I would recommend that you do not use the field macros at all, because they expand into too much bloat, and can be inflexible as you've discovered. Instead, implement the comparison and other basic operations with the do_* methods. So, you can define your own do_compare, and ensure the comparison uses === to consider X and Z states if that is what you want.
  18. Hi Cliff, It was a misspelling. Your config DB set is typed with virtual duf_if. It should be virtual dut_if. At least that should get you past that error.
  19. You can set a bit in each generated initial after putting the VIF into the config DB. Then, the initial that launches run_test can block until all bits have been set (wait on bitwise AND). That can ensure the build phases run after all VIF's have been put into the config DB.
  20. I don't think it needs to be that complicated. I think there are many ways to do error injection and detection. You can define some error fields as random variables within your sequence item. Then, design your driver to check the error field(s) settings, and drive an erroneous transaction on the bus accordingly. On the other side, you can design your monitor to detect bus-level errors, and set the appropriate error field(s) in the sequence item put together there before broadcasting to the analysis domain. I would just add the error-related functionality directly into your driver, monitor, and sequence item. The way I see it, errors are a basic attribute of any protocol. I'm not sure what you were trying to do with inheritance, but subclassing error-aware components from error-ignorant components seem unnecessary.
  21. Specifically, your class definition for bss_uvc_frmbuf_env should look something like this: class bss_uvc_frmbuf_env #(int MAX_LANES = 1) extends uvm_env; `uvm_component_param_utils(bss_uvc_frmbuf_env#(MAX_LANES)) ... endclass : bss_uvc_frmbuf_env You need to be able to register the specialization with the factory, so that you can use the factory create to build the instance of that specialized type.
  22. This code works perfectly fine for Questa and VCS (don't have access to NC). Only thing that I can guess about it is maybe it's something to do with SV 2009 vs. SV 2005, and whether your simulator supports 2009 or not. `uvm_do_with expands to `uvm_do_on_pri_with, which will expand and substitute in the constraint block containing `BLOCK_ADDR. Maybe this extra level of expansion is a limitation with 2005, but not for 2009. I don't know; I'm really guessing on this one. Besides, I think it's a better practice to define BLOCK_ADDR as either a UVM configuration variable (so that it can be modified at run time), or at least as a parameter. And, I think it's better to use start_item/finish_item instead of any of these `uvm_do_* macros, and do a randomize with after you start_item. In general, I'd avoid the preprocessor as much as possible, with a couple of exceptions (factory registration, UVM message reporting).
  23. I have an abstract agent that I wish to base other more concrete agents from, in order to use factory overriding within my configurable env. This abstract agent is defined to contain other components and objects such as the sequencer, subscriber, and configuration, and these are also all abstract classes. In addition, the abstract agent defines some boilerplate that uses the factory create to construct in the build phase, and connect in the connect phase. All pretty standard conventions that I wish to capture in the base classes. The thing I am uncertain about is that, if the env overrides the agent, would the subcomponents referenced in the abstract agent remain typed as the abstract classes? Do I need to add factory instance overrides to the concrete agent's build in order to ensure the subcomponents are concrete wherever they are referenced? What if the abstract classes are parameterized? Here is an abstract agent: virtual class abstract_agent#(T) extends uvm_agent; `uvm_component_param_utils(abstract_agent#(T)) uvm_analysis_port#(T) ap; abstract_config cfg; // assume this class defines is_active attribute abstract_subscriber#(T) sub; // assume this class also defines an analysis port ap abstract_sequencer#(T) seqr; function new(string name="abstract_agent", uvm_component parent=null); super.new(); endfunction : new function void build_phase(uvm_phase phase); super.build_phase(phase); uvm_config_db#(abstract_config)::get(this, "", "cfg", cfg); if(cfg.is_active == UVM_ACTIVE) seqr = abstract_sequencer#(T)::type_id::create("seqr", this); sub = abstract_subscriber#(T)::type_id::create("sub", this); ap = new("ap", this); endfunction : build_phase function void connect_phase(uvm_phase phase); super.connect_phase(phase); sub.ap.connect(ap); endfunction : connect_phase endclass : abstract_agent And, here is how I would like to define a typical concrete agent: class concrete_agent extends abstract_agent#(concrete_seq_item); `uvm_component_utils(concrete_agent) // use ap defined, built, and connected in abstract_agent concrete_config cfg; concrete_subscriber#(concrete_seq_item) sub; concrete_sequencer#(concrete_seq_item) seqr; function new(string name="concrete_agent", uvm_component parent=null); super.new(); endfunction : new function void build_phase(uvm_phase phase); super.build_phase(phase); // delegate build process to abstract_agent endfunction : build_phase function void connect_phase(uvm_phase phase); super.connect_phase(phase); // delegate connect process to abstract_agent endfunction : connect_phase endclass : concrete_agent Any ideas on a clean way to handle this situation? Thanks in advance!
  24. FWIW, OVL 2.6 (October 2011 release) is available at http://www.accellera.org/downloads/standards/ovl The fact that this was released less than a year ago suggests to me that there is still some active development going on under Accellera.
  25. If you want to do what you want to do (wait some amount of time based on your read response), then you can define a task in a configuration object that would call a task in the interface to wait a specified amount of time or cycles (assuming that the configuration object would also have the virtual interface handle). Your sequence can then get the configuration object handle via the sequencer, and call the wait task in the configuration object, which will in turn call the wait task in the interface at the physical level. This approach is actually well covered in Mentor's Verification Academy UVM Cookbook.
×
×
  • Create New...