Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by Erling

  1. Is this a bug or does the LRM state that an initializer for a static member is not in the scope of the member's class? Erling
  2. Is this really correct? The expression c=a+1 refers to a, and the rule is then that a must have been declared already. The initializer for a is part of the declaration of a, thus the value is known already to be 3 at point of declaration of c, and the initializer for c should therefore be 4. What do you mean the value could be 1? One should think that one reason for merging initialization with the declaration was to avoid this confusion. In C++ for example, where the two steps are separated, the initializers can be listed in an order that isn't the actual initialization order (which is the declaration order). It would seem that if the SV improvement in this regard comes with undefined initialization order, then this isn't an improvement after all, but a defect. Erling
  3. The slave monitor having a uvm_blocking_peek_imp means it is implementing a peek member task for someone else to call through a uvm_blocking_peek_port, which in this case sits in the sequencer. A sequence can then call peek() on the sequencer port, i.e p_sequencer.addr_ph_port.peek(). The peek call will be forwarded to the peek task of the monitor since the slave agent has connected the peek port of the sequencer to the monitor peek imp. At that point, peek() of the monitor will pend on the address_phase_grabbed event and return trans_collected to the sequence in the trans output parameter when the event triggers. Erling
  4. >But suppose i need the status of a particluar signal to cal a sequence inside a virtual sequencer, is there a way other than peek/poke the interface signals. Yes, the interface signals can be abstracted away. The status needed can be given a name, and providing that named status can be put as a requirement on the top level environment, i.e. simply assert that a testbench for my_dut shall provide status this and status that. When the test, in turn, is implemented by means of the testbench, it can just ask the testbench for status between sequences as needed, without direct reference to interface signals. Virtual sequences too can be implemented by means of the testbench, and can get hold of status in the same way a test would do. In this way, the testbench can use whatever method it likes to obtain the status, e.g. receive it through a port or event, or forward the status request to a component in the interface. The test or virtual sequence wouldn't notice the difference. Erling
  5. >If it does, then how create an instance of the semaphore via new()? Could you give the example codes? Just declare the semaphore with an initializer in the interface, for example: interface fifo_if ... semaphore lock = new(1); endinterface >The situation is like this, a_item is used to config the duv, b_item and c_item >are different types of packet, such as b_item is a "nap" packet, c_item is a >"dma" packet. but a_item, b_item and c_item all need to transfer to duv via the >same interface, which is fifo interface or pcie packet interface. Is it >difficult for uvm to solve such problem? There is no perfect solution to this problem. It is sometimes feasible to transfer different things through one representation, and sometimes not. If you choose different types extended from the agent transaction type, uvm may be of help at the sender side, as you can use the factory to override what's actually sent. Things will be serialized at the receiver side, since there is only one driver, but there will be downcast to the actual transaction type. To get started, it is probably easier to represent all packets types by one transaction type, i.e have the packet type a rand field of the transaction, and then choose packet type by randomizing the packet with the actual packet type. The receiver will have to use the packet type field to figure out how to interpret the rest of the request, again a cast, sort of, in the driver. You may want to try to get this up and running first, and then consider alternatives as needed. Erling
  6. >Maybe I can do like this: Individual semaphores on the items isn't going to help. If you want to serialize access to the interface, have a single semaphore in the interface for this purpose. >But I have one question about this, will the uvm report compile error or >runtime error while assigning data to the same interface's data in the write() >task in different drivers in uvm??? The last written data will be driven. >...how will the driver be blocked when it cann't get the semaphore while another driver is doing write task? Unless the drivers have idle work to do, they could simply block on the semaphore, ie use get() and not try_get. By the way, what makes you conclude that the transactions need to be of different types, ie what is special about a_item and b_item, so they can't be represented by my_item? Erling
  7. If the driver is parameterized on my_item, it can also accept any transaction extended from my_item. A sequence producing my_item's could be configured to produce some other item extended from my_item (by means of instance overrides for example). The driver would then have to downcast to determine the actual item type. Erling
  8. You could have a_item, b_item, etc extend from my_item, and transfer them as my_item through the agent, with the driver downcasting my_item to reveal the actual request. If the transactions aren't related in any way, it would seem better, as you point out, to have separate agents for them. Not sure what you mean by "how does it arbitrates the drivers". If the drivers are going to work with the same interface, but can't do that concurrently for some reason, a semaphore in the interface could be used to enforce serialization. Erling
  9. Yes, I'd hoped there were more than a lonely num I have many settings too, but they need not be visible in all sequences. If all knobs need to be visible everywhere, I'd suspect there is something unfortunate going on that isn't related to knobs. Also, if sequences indeed need access to many external settings, then it is always possible to aggregate and get hold of them in one operation. If you're concerned about performance, I'd recommend to fix the problem at one place, instead of distributing caches with direct access. Erling
  10. Not necessarily. Starting sequences could be delegated, without reference to sequencers. What often has to be dealt with, though, is the little things in between sequences, necessary to orchestrate operations, but it is not obvious to me that the sequencer should be involved in this (either). Another option could be to implement virtual sequences by means of the environment, and leave the sequencers an implementation detail. For example, if a piece of information is needed, and all the sequence has to talk to is the sequencer, then it is necessary for the environment to cache the information on the sequencer, and for the sequence to get hold of it via a dynamic downcast, i.e the p_sequencer solution. But if the sequence is implemented by means of the environment, on the other hand, it can go directly to the source, without casting or caching anything, and this would also work nicely in cases where the information isn't cacheable. What do you think, are there obvious problems with this solution? Other ideas? Erling
  11. It seems to me this would be reinventing global variables. I believe it is better to encapsulate related properties in components and sequences and strive to make them standalone and decoupled. If you need additional behavior later on, it may be better to extend instead of if-else-ing the original class on global knobs. Yes, but there are other solutions to this problem. For example, the good little monitor could handle interrupts as events, and transfer any additional info, if necessary, through the event. A sequence could fetch the event of interest from a pool setup by the environment. Things would be decoupled with no sequencer in between. Erling
  12. If num in your sample code is meant to be configurable, why not get hold of it explicitly at point of usage? This can be reduced to a one-liner with support from a common base class for sequences. The sub-sequence could go like this for example: class MySubSeq ...; rand int num; function void pre_randomize(); if (IsConfigInt("N", num)) num.rand_mode(0); endfunction endclass The inherited IsConfigInt() could first check the command line for +N=value, and if not found, try read an int resource named "N" in scope of the sequence. If a value was found in this way, randomization of num is turned off, otherwise num is randomized locally as usual. The top level virtual sequence could be responsible for coming up with a value for num, for example: class MyTopSeq ...; rand int num; function void pre_randomize(); if (IsUserInt("N", num)) num.rand_mode(0); endfunction function void post_randomize(); SetConfigInt("N", num); endfunction endclass The inherited IsUserInt() checks if there a command line value for num, and SetConfigInt() writes the actual num to a resource named "N" in scope of the sequence. The root component in the environment where the sequences shall run, could be responsible for creating the num resource with default value and scope to be matched by the sequences. This could happen in build or end_of_elaboration, so there will be a self-documenting list of system-wide settings at one place in the environment. I know this isn't rocket science exactly, but it is simple, explicit, and debuggable, and you can still choose to randomize sequences with inline constraints when this makes sense. Erling
  13. Why is it you want to have the config settings on the sequencer? What problem does this solve? Erling
  14. I recommand putting your own uvm survival kit in a package and document it. In this case you could have a common base for all sequences, and that base can override get_full_name() to return whatever dot-separted list of strings that makes sense for you. Erling
  15. This is better, now the sequence reads the config from a resource at the right place. It is just that we're back to the original problem, because get_sequencer() will return null in pre_randomize() unless the sequencer is explicitly set before randomize(), and this (as well as setting the parent sequence if any) can be done in a common base for reuse everywhere you want to start a sequence. With that in place, it shouldn't be necessary to read resources via the sequencer, because the sequence will have a proper scope and can work with resources directly without a helper component. Erling
  16. This solution randomize the knobs, not the sequence (ie. it does not have anything rand). Not sure how that is going to help. For example, if the knobs says we're in a certain mode, and the sequence shall be constrained according to the mode, then the knobs must be available at sequence pre/post/randomize(), it is too late to read them from a resource in the sequence body. Erling
  17. The sequence/sequencer relationship is established at sequence start (unless the sequencer is explicitly set on the sequence before start), and thus the sequence will be pretty much alone in space at randomize(). One way to deal with this is to write your own sequence start support where the sequencer (and parent sequence) connection is always set up before randomize. Another way to deal with the config is to have the sequence locate it, instead of pushing config via the sequencer. This way the sequence will be standalone and functional without external config and can be tested/debugged in isolation. In pre_randomize(), try read config from a resource (perhaps produced by another sequence), and/or the command line. If no overrides were found, then fine, have the randomizer produce settings locally. For each override found, turn off rand mode for the corresponding local member and set its value to the external config setting. Also, in post_randomize, check the command line and allow override of "everything" to enable complete control of settings (including illegal values), making it easier inject errors, override or determine constraints, debug etc. It may still be a good idea to establish the sequencer (and parent sequence) relationship before randomize(), in order to have a proper scope when dealing with resources in pre/post/randomize() (an alternative could be unique resource names and global scope, but this will break if you need to duplicate your environment). Erling
  18. The report ID's are local to a component (for some reason). You could use the hier-version of set_report_id_action(), but if you have look at how that works, you may want to override the report server process_report() instead, to filter out the PH_JUMP report. This can be useful for other things as well, for example to format the reports the way you like, get rid of the hierarchical reporting altogether, etc. Erling
  19. It seems the uvm_objection does not have callback support. Callbacks works with the uvm_callbacks_objection, which is derived from uvm_objection. Thus it seems hard to have the phase objections behave the way you want, since they are hardcoded as plain objections (for some reason). Erling
  20. I would guess my_objection_cbs_t should be uvm_objection_cbs_t. Erling
  21. It seems this is due to a simulator bug. Consider: class C1; int i; endclass class C2 #(type T) extends C1; endclass class C3 extends C2#(int); int i; function void f(); C1::i = 123; endfunction endclass With Questa, f() writes this.i, not C1::i. The statement: seq = uvm_sequence_base::type_id::create(); is slightly more complicated since uvm_sequence_base does not have a type_id, but it inherits one from uvm_sequence_item. Still, the type_id used (with Questa) is this.type_id, which is similar to f() above writing this.i. A possible workaround is to call create() on the type_id of the parameterized base, for example: task pre_body(); typedef uvm_sequence#(uvm_sequence_item) that; if (!$cast(seq, that::type_id::create("my_name"))) BigBadError(); endtask Now, the compiler will not use this.type_id, but that::type_id which is the type_id of uvm_sequence_item. Erling
  22. With this method, there will easily be an override entry for the "filter_me" report in most every component in the environment. Testing this out in a testbench resulted in set_report_severity_id_action_hier() calling itself 600 times for a single override, thus for a report with one source (for a given id), there will be an unused override in 599 maps in 599 report handlers. Also, if there are multiple report sources, all of them will be overridden, and it seems to be no reasonable way to target a specific source. Not sure what the rationale behind this is. Why not simply log everything through a single report object, i.e multiple report sources (with unique id's), but one reporter, one handler and one server? Is this limiting, i.e what useful feature is enabled by a forest of report handlers? Also, what useful feature is enabled by the report catchers, that isn't already provided by the report object api? Erling
  23. Problem with this is that activity being moved to the run phase will get the same starting point, and thus it may still be necessary to control the order of things by some other means. This is much more promising. I wonder why the phase*() interface is limited to components, though. It seems components does not actually need the *phase() interface, while any class could benefit from a phase*() interface. There may be good reasons for this being as it is, I don't know. BTW, I did some experimenting with custom phases and managed to corrupt the phase DAG. It may be totally unrelated, but it seems there is a problem in uvm_phase::add() when adding a phase in between phases. There is a check for the before phase being a successor to the after phase, it goes like this: if (after_phase.m_successors.exists(before_phase)) begin after_phase.m_successors.delete(before_phase); before_phase.m_successors.delete(after_phase); end One should think the after phase should be deleted from the before phase predecessors, not successors. Erling
  24. Exactly. And (as discussed in an other thread) it isn't necessary to call seq.start() in the test either, that can be done at the environment top-level. That way, tests will not have to peek inside the environment. Same with virtual sequences, and thus virtual sequencers need not be connected to sub-sequencers. As an example, consider a virtual sequence running two sequences in parallel: task body(); fork RunSequence(Seq1::Create()); RunSequence(Seq2::Create()); join endtask Here, the inherited RunSequence() locates the sequence starter in the testbench, which in turn performs sequence to sequencer mapping and starts the sequence. Have used this technique for months now and it works like charm. Erling
  • Create New...