Jump to content

Sequence with parameterized transactions


Recommended Posts

I am trying to create a small testbench, but it requires parameterized transactions.

My transaction is:

class clk_gen_cfg_transaction #(int CLK_HIGH_MAX = 64, int CLK_TOTAL_MAX = 128) extends uvm_sequence_item;

...

endclass

It has 9 properties, all registered with `uvm_field_int, except one that is `uvm_field_real.

I created a sequence that is just a randomization of that transaction:

class clk_gen_cfg_random_seq #(int CLK_HIGH_MAX = 64, int CLK_TOTAL_MAX = 128) extends uvm_sequence #(clk_gen_cfg_transaction #(CLK_HIGH_MAX, CLK_TOTAL_MAX));

`uvm_object_param_utils_begin(clk_gen_cfg_random_seq #(CLK_HIGH_MAX, CLK_TOTAL_MAX))

`uvm_object_utils_end

function new(string name = "clk_gen_cfg_random_seq");

super.new(name);

endfunction: new

clk_gen_cfg_transaction #(CLK_HIGH_MAX, CLK_TOTAL_MAX) tx;

virtual task body();

(1) tx = clk_gen_cfg_transaction #(CLK_HIGH_MAX, CLK_TOTAL_MAX)::type_id::create("clk_gen_cfg_random_seq_item");

(2)// assert($cast(tx, create_item(clk_gen_cfg_transaction #(CLK_HIGH_MAX, CLK_TOTAL_MAX)::get_type(), m_sequencer, "req")));

start_item(tx);

assert(tx.randomize());

finish_item(tx);

endtask: body

endclass: clk_gen_cfg_random_seq

Notice that I tried with (1) or (2), but the problem I am describing remains exactly the same.

My agent is very simple:

class clk_gen_cfg_agent #(int CLK_HIGH_MAX = 64, int CLK_TOTAL_MAX = 128) extends uvm_agent;

`uvm_component_param_utils(clk_gen_cfg_agent #(CLK_HIGH_MAX, CLK_TOTAL_MAX))

// external ports to connect to other components

uvm_seq_item_pull_port #(clk_gen_cfg_transaction #(CLK_HIGH_MAX, CLK_TOTAL_MAX)) seq_item_port;

uvm_analysis_port #(clk_gen_cfg_transaction #(CLK_HIGH_MAX, CLK_TOTAL_MAX)) analysis_port;

// internal components, built as configured

local clk_gen_cfg_driver #(CLK_HIGH_MAX, CLK_TOTAL_MAX) driver;

local uvm_sequencer #(clk_gen_cfg_transaction #(CLK_HIGH_MAX, CLK_TOTAL_MAX)) sequencer;

And this is what I have in my test case to configure the default sequence for the sequencer:

virtual function void build_phase(uvm_phase phase);

super.build_phase(phase);

// create the environment

e = clk_gen_env #(CLK_HIGH_MAX, CLK_TOTAL_MAX)::type_id::create("env", this);

uvm_config_db #(uvm_object_wrapper)::set(this, "env.clk_gen_cfg_agent.sequencer.run_phase", "default_sequence", clk_gen_cfg_random_seq #(CLK_HIGH_MAX, CLK_TOTAL_MAX)::type_id::get());

endfunction: build_phase

When I run this testbench, I get a fatal message:

UVM_FATAL @ 20000: uvm_test_top.env.clk_gen_cfg_agent.sequencer [sequencer] send_request failed to cast sequence item

At first I thought that I was using a wrong parameter somewhere, but after some time I couldn't find any problem. Then I decided to look from where this message is coming from.

It comes from file uvm_sequencer_param_base.svh, method send_request, line 291.

The code is this:

REQ param_t;

if ($cast(param_t, t)) begin

if (rerandomize == 1) begin

if (!param_t.randomize()) begin

uvm_report_warning("SQRSNDREQ", "Failed to rerandomize sequence item in send_request");

end

end

if (param_t.get_transaction_id() == -1) begin

param_t.set_transaction_id(sequence_ptr.m_next_transaction_id++);

end

m_last_req_push_front(param_t);

end else begin

uvm_report_fatal(get_name(),$psprintf("send_request failed to cast sequence item"), UVM_NONE);

end

So, apparently param_t and t are of different types, which reinforced the notion that I could be passing an object of wrong type somewhere.

But I decided to print both objects to check which types are they. I inserted this code on that file:

param_t = REQ::type_id::create("test");

assert(param_t.randomize());

`uvm_info("debug", t.sprint(), UVM_MEDIUM)

`uvm_info("debug", param_t.sprint(), UVM_MEDIUM)

I had to create and randomize param_t or else the pointer would not be defined and simulation exit.

What I received in the screen was:

# UVM_INFO ../../Testbench/clk_gen_sv_tb/Agents/clk_gen_cfg_agent/clk_gen_cfg_agent.sv(55) @ 20000: uvm_test_top.env.clk_gen_cfg_agent.sequencer [debug] ---------------------------------------------------------------------------------------------------------------

# Name Type Size Value

# ---------------------------------------------------------------------------------------------------------------

# clk_gen_cfg_random_seq_item uvm_sequence_item - @704

# clk_high integral 7 'd44

# clk_total integral 8 155

# clk_dist integral 8 'd13

# clk_offset integral 2 3

# clk_typ integral 2 'd1

# num_clock_cycles integral 32 'd70466

# clk_relation real 64 89.478528

# num_expected_transitions_min integral 32 'd787

# num_expected_transitions_max integral 32 'd788

# begin_time time 64 20000

# depth int 32 'd2

# parent sequence (name) string 9 <unknown>

# parent sequence (full name) string 54 uvm_test_top.env.clk_gen_cfg_agent.sequencer.<unknown>

# sequencer string 44 uvm_test_top.env.clk_gen_cfg_agent.sequencer

# ---------------------------------------------------------------------------------------------------------------

#

# UVM_INFO ../../Testbench/clk_gen_sv_tb/Agents/clk_gen_cfg_agent/clk_gen_cfg_agent.sv(56) @ 20000: uvm_test_top.env.clk_gen_cfg_agent.sequencer [debug] ---------------------------------------------------------------------------------------------------------------

# Name Type Size Value

# ---------------------------------------------------------------------------------------------------------------

# test uvm_sequence_item - @732

# clk_high integral 7 'd9

# clk_total integral 8 'd66

# clk_dist integral 8 'd12

# clk_offset integral 2 'd0

# clk_typ integral 2 'd0

# num_clock_cycles integral 32 'd57767

# clk_relation real 64 20.139785

# num_expected_transitions_min integral 32 'd2868

# num_expected_transitions_max integral 32 'd2869

# ---------------------------------------------------------------------------------------------------------------

So, it seems that both are really the type of my object. However, the first one has 5 extra properties, which definitively wasn't me who put them, they are inserted by the sequence I guess.

What can be the problem, and how to correct it?

I am using QuestaSim 10.0a

Thanks in advance for any help.

Link to comment
Share on other sites

The OVM/UVM factory automation / macros have limitations; it is unfortunate that the presence of a macro implies that the technique you're experimenting with is useful or appropriate. I don't think parameters are the right solution for you here for what is really a runtime soft configuration requirement.

The best advice I can give you is to keep your data/sequence objects un-parameterized and use soft configuration instead. They are intended to be abstractions after all. Only parameterize your structural elements (i.e. the DUT, its interfaces, the env/agents/monitors/drivers that connect to them) but not your data/sequence/scoreboards/coverage.

In your particular case, you are using parameters to specify some behavior (e.g. a constraint on a clock high / low time period?) which you should instead set either by constraints or randomize..with{} or by extending a sequence and adding a constraint or by giving sequence access to an env-wide configuration resource. In each case you are using a soft technique (i.e. runtime integers) to solve this problem rather than compile/elaboration-time overhead and complexity of parameters.

Just my opinion...

Link to comment
Share on other sites

To run a parameterized sequence to run, you have to start it on the sequencer. To get your code to run :

1) add the following macro to the sequence :

`uvm_declare_p_sequencer(you_sequencer_type_t)

2) create and start the sequence in your test :

your_sequence_type seq=...create("s0");

seq.start(your_env.your_agent.your_sequencer);

This was all right on OVM and UVM-1-EA and should be compatible on UVM-1.0p1. There may be a new way of doing the same thing though now in UVM1.0p1

Link to comment
Share on other sites

hi,

i agree with gordon here. you should NOT use parameters in places where a runtime constant or field is good enough. making it a parameter is only required if you need compile time or elab time constants (bit width or similar). parameters are fine for certain things but they come with the downside that paramterized classes are only assignment compatible if ALL parameters are the same.

Link to comment
Share on other sites

To run a parameterized sequence to run, you have to start it on the sequencer. To get your code to run :

1) add the following macro to the sequence :

`uvm_declare_p_sequencer(you_sequencer_type_t)

2) create and start the sequence in your test :

your_sequence_type seq=...create("s0");

seq.start(your_env.your_agent.your_sequencer);

This was all right on OVM and UVM-1-EA and should be compatible on UVM-1.0p1. There may be a new way of doing the same thing though now in UVM1.0p1

I tried this, but I get the message:

"Dotted name ('env.clk_gen_cfg_agent.sequencer') not found in current scope. Note that hierarchical references are not allowed from within a package or $unit."

I guess it is because we put our test lib inside of a package. Should we also avoid this? What is the common practice to manage the test lib?

Link to comment
Share on other sites

hi,

i agree with gordon here. you should NOT use parameters in places where a runtime constant or field is good enough. making it a parameter is only required if you need compile time or elab time constants (bit width or similar). parameters are fine for certain things but they come with the downside that paramterized classes are only assignment compatible if ALL parameters are the same.

Those parameters were actually bit widths, I used internal variables: "rand bit [log2(CLK_HIGH_MAX)-1:0] clk_high;" and so on. But I changed them to int and limited them with constraints instead. Now the transactions have no parameters.

However, the problem is still happening. It seems that those 5 extra fields are being created in one instance of the class, but not in the other:

# begin_time time 64 20000

# depth int 32 'd2

# parent sequence (name) string 22 clk_gen_cfg_random_seq

# parent sequence (full name) string 67 uvm_test_top.env.clk_gen_cfg_agent.sequencer.clk_gen_cfg_random_seq

# sequencer string 44 uvm_test_top.env.clk_gen_cfg_agent.sequencer

Link to comment
Share on other sites

Update:

Probably these 5 fields are not the problem, as they also exist just in one of the instances in the ubus example, but the cast suceeds. Is there any other reason that a $cast can fail other than different types. For example, the sequencer is trying to cast between those objects, but fails:

# UVM_INFO ../../Testbench/clk_gen_sv_tb/Agents/clk_gen_cfg_agent/clk_gen_cfg_agent.sv(59) @ 20000: uvm_test_top.env.clk_gen_cfg_agent.sequencer [debug] ----------------------------------------------------------------------------------------------------------------------------------

# Name Type Size Value

# ----------------------------------------------------------------------------------------------------------------------------------

# tx clk_gen_cfg_transaction - @704

# clk_high integral 8 'd4

# clk_total integral 8 237

# clk_dist integral 8 'd69

# clk_offset integral 2 2

# clk_typ integral 2 'd1

# num_clock_cycles integral 32 'd60759

# num_expected_transitions_min integral 32 'd0

# num_expected_transitions_max integral 32 'd0

# begin_time time 64 20000

# depth int 32 'd2

# parent sequence (name) string 22 clk_gen_cfg_random_seq

# parent sequence (full name) string 67 uvm_test_top.env.clk_gen_cfg_agent.sequencer.clk_gen_cfg_random_seq

# sequencer string 44 uvm_test_top.env.clk_gen_cfg_agent.sequencer

# ----------------------------------------------------------------------------------------------------------------------------------

and

# UVM_INFO ../../Testbench/clk_gen_sv_tb/Agents/clk_gen_cfg_agent/clk_gen_cfg_agent.sv(60) @ 20000: uvm_test_top.env.clk_gen_cfg_agent.sequencer [debug] ----------------------------------------------------------------------------------------------------------------------------------

# Name Type Size Value

# ----------------------------------------------------------------------------------------------------------------------------------

# test clk_gen_cfg_transaction - @732

# clk_high integral 8 143

# clk_total integral 8 135

# clk_dist integral 8 'd3

# clk_offset integral 2 'd1

# clk_typ integral 2 'd1

# num_clock_cycles integral 32 'd18481

# num_expected_transitions_min integral 32 'd0

# num_expected_transitions_max integral 32 'd0

# ----------------------------------------------------------------------------------------------------------------------------------

The type is exactly the same (clk_gen_cfg_transaction).

Link to comment
Share on other sites

OK, found out the problem, it is not related with UVM, but with SV packages.

The problem was that clk_gen_cfg_transaction was included in 2 different packages, then Questa was creating two different classes exactly equal, but that could not cast between themselves. It could have emitted a warning to tell me before starting the simulation. Anyone confirm if that is the case also in other simulators?

What is the recommended way to manage the packages with transactions? Do you have a separate package for each transaction, one package for all transactions? I was not having a dedicate package for transactions, but then included it in the sequences package and agent package, where it was both needed.

Link to comment
Share on other sites

I had a gut feel this might be a package problem, I've seen this `include problem before - but I was thrown off by the 5 fields discrepancy.

This is a common problem - and note that a simulator would not normally emit a warning for duplicate class names (that's what package namespaces is intended to allow). And by $cast time the relevant information is often optimized out. I wish it were easier to spot this.

My recommendation for package management on your UVC: have two packages: one for the UVC structure and one for its data:

Data package:

//my_data.svh

package my_data;
  class my_item extends uvm_sequence_item;
    ...
  endclass
endpackage

UVC package:

//my_uvc.sv

  `include "my_interface.svh"
  `include "my_data.svh"

package my_uvc;
  import uvm_pkg::*; `include "uvm_macros.svh"
  import my_data::*;
  `include "my_config.svh"
  `include "my_sequencer.svh"
  `include "my_driver.svh"
  `include "my_monitor.svh"
  `include "my_coverage.svh"
  `include "my_agent.svh"
endpackage

That way you can reuse the 'my_data' package elsewhere e.g. in a scoreboard in your env, hooked to the analysis port on your UVC. Those analysis components should import the data package, not the full UVC package. Circular dependencies can be avoided.

Put further transactions or sequences which extend the base data type either in the UVC package or in a third my_seqlib package to suit your component's needs and dependencies. But keep the my_data package to just the minimum necessary for reuse, normally a single class.

Link to comment
Share on other sites

  • 1 year later...

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...