Duplicate items in uvm_tlm_analysis_fifo used in scoreboard

Hi,
I’ve been spent days and days debugging this problem with no luck.
I have a scoreboard where I’m trying to log outbound packets on a switch with multiple output links. I do the following in the scoreboard:


...
// in the run_phase:
// Output packets thread
      begin
        fork  
          begin
            for (int i = 0; i < num_outs; i++) begin
              fork
                automatic int j = i;
                out_pkt_thread(j);
              join_none
            end
            wait fork;
          end
        join
      end
...
// fifo_out and pkt_out_actl_q in the scoreboard class
    //declarations
    uvm_tlm_analysis_fifo #(pkt_t)      fifo_out[];
    pkt_t                               pkt_out_actl_q[][$];

    // in build phase
    fifo_out        = new[num_outs];
    foreach (fifo_out[i]) begin
      fifo_out[i]      = new($sformatf("fifo_out[%0d]", i), this);
    end
    // in connect phase
    foreach (axp_pkt_out[i]) begin // axp_pkt_out has the type uvm_analysis_export
      axp_pkt_out[i].connect(fifo_out[i].analysis_export);
    end
    // in run phase
    pkt_out_actl_q = new[num_outs];

...
// definition of out_pkt_thread
  task out_pkt_thread (int link_id);

    forever begin
      pkt_t pkt_out_clone = pkt_t::type_id::create("pkt_out_clone", this);
      pkt_t pkt_item;

      fifo_out[link_id].get(pkt_item);
      // I tried doing a #1ps here

      `uvm_info($sformatf("SCOREBOARD: Read a packet out[%0d]:", link_id),  pkt_item.convert2string(), UVM_LOW)
      // Only queue if the scoreboard is enabled
      if (!m_env_cfg.disable_sb) begin
        pkt_out_clone.copy(pkt_item);
        pkt_out_actl_q[link_id].push_back(pkt_out_clone);
      end // if sb enabled
    end // forever

  endtask

The scoreboard is failing to log some of the packets on the output packets properly. If I hook things up to a file log subscriber instead of axp_pkt_out, I can see everything logged properly. Which means the problem is in the code provided here. Some of the packets are logged correctly in the queue but some are overwritten by another packet. Here’s some examples from the transcript:


/*
# UVM_INFO scoreboard.svh(311) @ 20328742000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet in[0]:] packet A info
# UVM_INFO scoreboard.svh(311) @ 20330094000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet in[0]:] packet B info
# UVM_INFO scoreboard.svh(311) @ 20330769000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet in[0]:] packet C info
# UVM_INFO scoreboard.svh(311) @ 20331445000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet in[0]:] packet D info
# UVM_INFO scoreboard.svh(311) @ 20332797000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet in[0]:] packet E info

# UVM_INFO scoreboard.svh(358) @ 20335210000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet out[0]:] packet C info
# UVM_INFO scoreboard.svh(358) @ 20335210000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet out[0]:] packet C info
# UVM_INFO scoreboard.svh(358) @ 20335210000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet out[0]:] packet C info
# UVM_INFO scoreboard.svh(358) @ 20335548000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet out[0]:] packet E info
# UVM_INFO scoreboard.svh(358) @ 20335548000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet out[0]:] packet E info
*/

Packets may arrive at the same time on one link, that’s allowed… The number of packets on each link for the predicted and actual packets are equal. I get a bunch of mismatches when I compare predicted packets to actual ones, so they are getting queued wrong as the print statements show.

Thanks!

In reply to bmaassar:

What is the objective of this piece of code?

// Output packets thread
      begin
        fork  
          begin
            for (int i = 0; i < num_outs; i++) begin
              fork
                automatic int j = i;
                out_pkt_thread(j);
              join_none
            end
            wait fork;
          end
        join
      end

I do not understand your fork/join constructs. Could you please explain what is the reason for this.

UVM_INFO scoreboard.svh(311) @ 20328742000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet in[0]:]

Is indicating there is no link_id.
The TLM scoreboard does not know anything about time. It knows only the order of your data.
Something like ‘# 1ps’ does not help here.

In reply to chr_sue:
Thank you for the quick reply.
The intent is to log packets on all output links in parallel, there’s a fifo and queue associated with each link based on index link_id so it is passed. The first begin is there because I run this “output packets thread” in parallel with an “input packets thread” using another fork-join not shown. The fork-join in the shown piece code might be redundant; it was added while debugging just in case.
The link_id is in the in the printout. In the example shown it’s zero and all packets go come out of the same link. I see different values being printed in my transcript so it’s being passed correctly.

I just tried the following btw:
After fifo_out[link_id].get(pkt_item); in the provided code, I do


fifo_out[link_id].get(pkt_item);
if (fifo_out[link_id].try_peek(pkt_item2))
 `uvm_info($sformatf("SCOREBOARD: peeking at fifo %0d after a get", link_id), pkt_item2.convert2string(), UVM_LOW)
    else
          `uvm_info("SCOREBOARD:", "try_peek failed", UVM_LOW)

      #1ns;
      `uvm_info($sformatf("SCOREBOARD: Read a packet out[%0d]:", link_id),  pkt_item.convert2string(), UVM_LOW)

Result sample:


# UVM_INFO scoreboard.svh(458) @ 2341646000: uvm_test_top.m_env.sb [SCOREBOARD: peeking at fifo 0 after a get] packet A info
# UVM_INFO scoreboard.svh(463) @ 2341647000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet out[0]:] packet A info
# UVM_INFO scoreboard.svh(460) @ 2341647000: uvm_test_top.m_env.sb [SCOREBOARD:] try_peek failed
# UVM_INFO scoreboard.svh(463) @ 2341648000: uvm_test_top.m_env.sb [SCOREBOARD: Read a packet out[0]:] packet A info

So there must be something wrong with that fifo_out and/or axp_pkt_out!

In reply to bmaassar:

Your out_pkts should come from one or more minitors observing the traffic and putting the extracted data using the write or a put method. In the scorboard you have to perfrom gets on the analysis_fifos and compairing your data. I believe your approach for generating the scoreboard data is not clean.
One indication is shown in your log-file:
[SCOREBOARD: Read a packet out[0]:]
means there are no data avalable. You are perfroming the non-blocking try_get. This is your problem. Using the blocking get might solve your problem.

In reply to chr_sue:
Yes, so out_pkts come from an agent.monitor.port. I connected this agent.monitor.port to file log subscribers and the outputs to the files look correct. Then, there’s no problem with the port.

Each agent.monitor.port is connected to the analysis_export of a subscriber (part of the scoreboard) extended from the uvm_subscriber class. I added prints for every write that is being done and I don’t see duplicates. Then, there shouldn’t be a problem with the subscriber.

The uvm_analysis_port of the subscriber is connected to the analysis_export of the fifo_out, and as mentioned, the try_peek exposes the duplicates in the fifo, so it must be one of the fifo and/or its axp!

I’m only showing the parts of the code where the packets are monitored and logged on the output. The input generation, monitoring and prediction work fine.

There is data available in the log-file, for the purposes of the blog I wrote “packet A info”, instead of the actual data, which is there…

I’m using get, as shown in the code provided in the question:
fifo_out[link_id].get(pkt_item);

I think, you might have to look at the component where you are putting the data into fifo.
You might need to create another packet for the handle each time you perform a *_fifo.put().

Your output indicates that 3 packets come with the same content. Not sure if this might solve your problem.

If possible, can you please put in the code, where you put into the FIFO, probably in the monitor.

In reply to bmaassar:

In reply to chr_sue:
Yes, so out_pkts come from an agent.monitor.port. I connected this agent.monitor.port to file log subscribers and the outputs to the files look correct. Then, there’s no problem with the port.
Each agent.monitor.port is connected to the analysis_export of a subscriber (part of the scoreboard) extended from the uvm_subscriber class. I added prints for every write that is being done and I don’t see duplicates. Then, there shouldn’t be a problem with the subscriber.
The uvm_analysis_port of the subscriber is connected to the analysis_export of the fifo_out, and as mentioned, the try_peek exposes the duplicates in the fifo, so it must be one of the fifo and/or its axp!
I’m only showing the parts of the code where the packets are monitored and logged on the output. The input generation, monitoring and prediction work fine.
There is data available in the log-file, for the purposes of the blog I wrote “packet A info”, instead of the actual data, which is there…
I’m using get, as shown in the code provided in the question:
fifo_out[link_id].get(pkt_item);

I guess your problem is caused by the try functions. Becaus it looks only if there is an item or not. Using get is always waiting for the next item. It is blocking. Using try_peek or try_get might show the same item several times.

In reply to Ashith:
From my debugging the problem is in uvm_tlm_analysis_fifo #(pkt_t) fifo_out; (see my last reply)

In reply to chr_sue:
I’m only using try functions to debug and print things out. I’ve made a custom uvm_tlm_analysis_fifo based on the uvm one. I added print statement in the write function:


class bvm_tlm_analysis_fifo #(type T = int) extends uvm_tlm_fifo #(T);

  // Port: analysis_export #(T)
  //
  // The analysis_export provides the write method to all connected analysis
  // ports and parent exports:
  //
  //|  function void write (T t)
  //
  // Access via ports bound to this export is the normal mechanism for writing
  // to an analysis FIFO. 
  // See write method of <uvm_tlm_if_base #(T1,T2)> for more information.

  uvm_analysis_imp #(T, bvm_tlm_analysis_fifo #(T)) analysis_export;


  // Function: new
  //
  // This is the standard uvm_component constructor. ~name~ is the local name
  // of this component. The ~parent~ should be left unspecified when this
  // component is instantiated in statically elaborated constructs and must be
  // specified when this component is a child of another UVM component.

  function new(string name ,  uvm_component parent = null);
    super.new(name, parent, 0); // analysis fifo must be unbounded
    analysis_export = new("analysis_export", this);
  endfunction

  const static string type_name = "bvm_tlm_analysis_fifo #(T)";

  virtual function string get_type_name();
    return type_name;
  endfunction

  function void write(input T t);
    my_pkt t1;

    if($cast(t1, t)) begin
      if(this.try_put(t1)) // unbounded => must succeed
            `uvm_info("SB: packet written to fifo:", t1.convert2string(), UVM_LOW)
      else
            `uvm_info("SB:", "try_put failed", UVM_LOW)
    end // if($cast(t1, t))   
    else begin
      `uvm_fatal(get_type_name(), "Unsupported UVM transaction format!");
    end
  endfunction

endclass


Result is try_put always succeeds. I print out unique packets (no duplicates) with the “packet written to fifo” statement. But the duplicates are still written to the fifo! So, again, I really think it’s this uvm fifo and it’s frustrating.

In reply to bmaassar:

Do You believe this is the right approach?
Where are you facing the issue, in your individual model or in the original one?

In reply to chr_sue:
Yes, because I’ve debugged and isolated the problem to this fifo. The original uvm_tlm_analysis_fifo had the issue, which is why I have my own and it has the issue too…

In reply to bmaassar:

I’m working sice 10 years in the OVM/UVM space and have mde numerous projects. I have heard or faced an issue withh the tlm_analysis fifo. I belive the error coms from omewhere else. I’d not rely on your approach
BTW if you want to check if there is an item you can use peek. This does not remove the item, but it is blocking until an item is available.

In reply to Ashith:

I finally found and fixed the bug last week. In a way, you were right.

The object wasn’t being cloned in the monitor (cloning it was the fix) and I wasn’t aware that the different connected uvm elements were all passing around handles rather than objects (rookie mistake). I didn’t suspect the monitor because it was printing out debugging message and everything looked fine, but of course with handles being passed, the object can and did get overwritten after the printouts. I didn’t write the monitor code and it was supposed to be tested and working by someone else… That was misleading…