Parametrized agent

Greetings,

Lets imagine some form of advanced switch IP. This IP has all its inputs and outputs in form of one interface (lets say it is something similar to AXI stream). However, each of these interfaces has different bitwidth ranging from 1024 bits to 8 bits.

I would like to have one agent class (sequencer, driver, monitor, scoreboard, etc…) and specify the bitwidth as its parameter somewhere during the construction of the agent. So I can just instantiate the agent and connect in in the environment.

I tried to have BITWIDTH as a parameter, but it leads to parametrized transaction (because transaction resents logic [BITWIDTH-1:0] data[$]). And this approach fails on the finish_item call which attemps illegal casting of parametrized transaction.

I could create transaction class which will have data[1023:0] and use only portion of the allocated memory[7:0], but when we take inco account, that we can have several thousands words inside the transaction, we can end with wasting a large amount of memory. Therefore I would like to avoid this approach if possible.

I would like to ask for pointer how to achieve this and if it is even possible with UVM.

Thank you for any suggestions,
Jan

In reply to galloth:

I see 2 options, either you are using a package to hold the actual parameter and you are importing this package in each class you need it. Or you are using macros to differentiate between the different cases.

In reply to chr_sue:
Hi,
could you please elaborate these aproaches a little?

Lets say, I have 4 interfaces in the environment class:


my_interface#(.BIT_WIDTH(32)) vif1;
my_interface#(.BIT_WIDTH(1024)) vif2;
my_interface#(.BIT_WIDTH(8)) vif3;
my_interface#(.BIT_WIDTH(17)) vif4;

Ideally, I was hoping for saying


my_agent#(.BIT_WIDTH(32)) agent1;
my_agent#(.BIT_WIDTH(1024)) agent2;
my_agent#(.BIT_WIDTH(8)) agent3;
my_agent#(.BIT_WIDTH(17)) agent4;

I am not sure how to use package or macros inside the class simulate that behavior. I would be able to use both approaches if there would be only one type of the agent in environment, but since the whole point is to have several different bit_widths at once.

I am looking at this issue like learning excercise in the way that I would like to know the “correct” way to deal with this instead of the fastest way. So I thank you for any proposal or pointer.

Thank you
Jan

In reply to galloth:

I was assuming you do not need the agents/interfaces with different parameters at the same time.
What you are proposing is a possible solution.

In reply to chr_sue:

Thank for your answer. I tried to implement this approach and it seemed to be working up to the moment when I tried to parametrize transaction. The issue I am facing is that since I do not have much assumption on the content of the transfer I have transaction which contain data as an array [BIT_WIDTH-1:0] data [$] . Therefore I can have straightforward monitor/driver.

The problem appears in sequence, where I created instance of


fb_uvm_sample_stream_transaction#(int BIT_WIDTH=32) extends uvm_sequence_item;

inside my sequence (lets say SEQUENCE_BIT_WIDTH is 64)


    trn = fb_uvm_sample_stream_transaction#(.BIT_WIDTH(SEQUENCE_BIT_WIDTH))::type_id::create(
    .name("sqv"),
    .contxt(get_full_name()));
    start_item(trn);
    assert(trn.randomize()) else `uvm_fatal("SAMPLE_SQV","Randomization failed");
    finish_item(trn);

when finish_item(trn) fails since it contains illegal cast.

Currently, I have parametrized agents but have one transaction which has maximal possible BIT_WIDTH allocated (thus wasting a lot of memory) and some additional logic to ignore unused memory in driver, and in the transaction itself. I am not happy with this solution since

  • it requires the additional configuration of the transaction object in the sequence after creation
  • The BIT_WIDTH is variable and as such it can not be use at all places (for example data[BITH_WIDTH-1:0] is invalid since it leads to the variable range)
  • Further complicates the driver (must ask transaction which bits are valid and which not)

Each of these issues are solvable but the questing that raises is if this is the right way to go? Or is there some other way how to build it in UVM which would be preffered? I guess I am hoping that I miss some obvious solution or language construct that would allow me to have parametrized the transaction as well.

Thank you again
Jan

In reply to galloth:

The construction of your transaction is wrong. It should be
trn = fb_uvm_sample_stream_transaction#(.BIT_WIDTH(SEQUENCE_BIT_WIDTH))::type_id::create(“trn”);
Do you have different BIT_WIDTH for the transaction and the sequence?