Can monitor/scoreboard get input data from sequencer?

I am working on verification plan at FPGA top level. Input data is a serial bit(SPI if) and goes through different modules/stages and comes out as 25X2 bytes( declared as bit [24:0][15:0] rd_data;). I have few questions:

  1. There is, variable huge delays(intended) in the modules and it may be difficult to calculate exact time the data comes out with respect to input. In that case, can we collect input data from sequencer to scoreboard? If that is not the recommended way, can we have req, rsp separately and in the driver, just send the input data through TLM analysis port? Or is there any other better approach?
  2. It is hard to capture/sample FPGA output data at right time, since there is no data_valid or any other indication. How do we handle it in such cases?

In reply to uvmsd:

  1. There is, variable huge delays(intended) in the modules and it may be difficult to calculate exact time the data comes out with respect to input. In that case, can we collect input data from sequencer to scoreboard? If that is not the recommended way, can we have req, rsp separately and in the driver, just send the input data through TLM analysis port? Or is there any other better approach?
  1. It is hard to capture/sample FPGA output data at right time, since there is no data_valid or any other indication. How do we handle it in such cases?

(1) In principle it is possible to send data from the sequence to any component. But it is not recommended, because it restricts the reusibility. Of course you can have a req and a rsp in the driver. Make sure the rsp gets the same id as the corresponding req has.
(2) If you do not have any indication when your output data starts you cannot verify your design. But I believe you have an indication, like start frame indication or what else.

In reply to chr_sue:

Thank you!

  1. I went through bidirectional sequencer/driver. It doesn’t really serve my requirement. RSP is used only if sequencer is waiting for acknowledgement or so from DUT to send next sequence. I don’t have such requirement. My only challenge is predicting the timing of the output.
  2. Yes, there is no indication for output data. One way I can think of is, wait for a similar delay(as in RTL) in driver. (This is what i have done in module level testing). But there is one more module that provides read_addr to the RAM. Since it generates read_addr continuously, its hard to predict when exactly the real output data comes out.
    Probably, I can use the rd_addr generated in RTL. Can I access DUT internal signals on TB side just by their path? Eg. $root.dut_top.module1.module2.sig1

I saw some other post in the forum and there was a suggestion to use interface to access the DUT internal signal. Interface will have access to DUT top signals(or ports) right? can we access internal signals? May be, i need to look into it more.

In reply to uvmsd:

To be honest if you doo not have any indication you will not be able to evaluate your output data. Such an indication could be also available inside your design. Then you can bind an sertion module to your DUT, observing internal states and trigger an avent when the internal state appears. This event can be used start wiith the evaluation of your output data.

In reply to chr_sue:

In reply to uvmsd:
To be honest if you doo not have any indication you will not be able to evaluate your output data. Such an indication could be also available inside your design. Then you can bind an sertion module to your DUT, observing internal states and trigger an avent when the internal state appears. This event can be used start wiith the evaluation of your output data.

@chr_sue, thank you! I am trying something similar.

module ov1_top 
  //internal signals 
  :
  :
     RAM  inst_RAM_awg (
              .clk     (system_clk),
              .wr      (awg_wr_en),
              .wr_addr (awg_wr_addr),
              .wr_data (spi_dout),
              .rd_addr (awg_rd_addr),
              .rd_data (awg_rd_data)
            );        
      WAVE_GEN inst_WAVE_GEN (
          .clk     (system_clk_spi),
          .clk_en  (clk_en_640kHz),
          .rst_n   (sync_n_rst),
          .rd_addr (awg_rd_addr)
        );
endmodule
//interface
interface dut_sig_if(
  input logic [4:0] awg_rd_addr ,
  input logic [399:0] awg_rd_data
  );  
endinterface
moduel tb_ov1_top;
//internal signals
//clk, reset generation

ov1_top inst_OV1_TOP(
  :
  :

  );
  //bind
  bind WAVE_GEN : inst_OV1_TOP.inst_WAVE_GEN dut_sig_if awg_if(.awg_rd_addr(awg_rd_addr));

  initial begin
  :
      uvm_config_db#(virtual dut_sig_if)::set(null, "*","wave_gen_if", inst_OV1_TOP.inst_WAVE_GEN.awg_if);

  :
  emd
endmodule
// access it in monitor using
    if(!uvm_config_db#(virtual dut_sig_if)::get(this,"","wave_gen_if",awg_if))
      `uvm_fatal("NO_VIF",{"WAVEGEN interface must be set for: ",get_full_name(),".awg_if"});

The above bind accesses one internal signal from one internal module. If i want to access other internal signals (ports of RAM), can i do it using single bind, as below?
Can i bind all the internal signals of ov1_top(dut_top)?
bind ov1_top: inst_OV1_TOP dut_sig_if awg_if(.awg_rd_addr(awg_rd_addr),.awg_rd_data(awg_rd_data)));

and
uvm_config_db#(virtual dut_sig_if)::set(null,“*”,“wave_gen_if”,inst_OV1_TOP.awg_if);

Sorry, if there are any syntax and other errors. Just wanted to put the relevant code here.
Thanks in advance.

In reply to uvmsd:

One bind can connect a module/interface to a design or an instance, i. e. you need for each module/instance you want to connect to a seperate bind. For toplevel signals you do not need the bind because the signals are directly visible.

In reply to chr_sue:

oh!ok. Thank you!
I need to read ram output:

generate
        for (g = 0; g < 25; g++) begin 
          RAM inst_RAM_awg (
              .clk     (clk),
              .wr      (awg_wr_en[g]),
              .wr_addr (awg_wr_addr),
              .wr_data (spi_dout[g]),
              .rd_addr (awg_rd_addr),
              .rd_data (awg_rd_data[g])
            );
        end
      endgenerate

Do i need to have as many bind as ram instances to get complete awg_rd_data?
Can I use same interface for binding signals from different sub modules? For eg: i want to get signals from wave_gen module and also from RAM module, can i use a single interface for those signals?
Does bind needs to be used only for outputs of a module? Can i use to read input signals to a module? In above case, output of wavegen module is nothing but input to RAM module. So, I can just bind RAM and get both rd_addr and rd_data.

In reply to uvmsd:

You can use the same interface but you need as many nstances as RAM instances.
You can read any signals in your design. There is no limitations. Only variables in a VHDL design cannot be read.

In reply to chr_sue:

Thanks chr_sue,

One more thing, was just thinking to access, awg_rd_data,awg_rd_addr directly from the DUT top file.

DUT top file is as below:

module OV1_top;
  :
  :
  logic [24:0][15:0] awg_rd_data;
  logic [4:0] awg_rd_addr;
      generate
        for (g = 0; g < 25; g++) begin 
          RAM inst_RAM_awg (
              .clk     (system_clk_spi),
              .wr      (awg_wr_en[g]),
              .wr_addr (awg_wr_addr),
              .wr_data (spi_dout[g]),
              .rd_addr (awg_rd_addr),
              .rd_data (awg_rd_data[g])
            );
        end
      endgenerate
  :
  :
endmodule

module tb_ov1_top
  :
  :
OV1_TOP inst_OV1_top (
 :
 :
);
bind OV1_TOP : inst_OV1_top dut_sig_if awg_if(.awg_rd_addr(awg_rd_addr), .awg_rd_data(awg_rd_data));
 initial begin
  :
      uvm_config_db#(virtual dut_sig_if)::set(null, "*","wave_gen_if", inst_OV1_TOP.inst_WAVE_GEN.awg_if); 
  end
endmodule

Please note that the awg_rd_data & awg_rd_addr in DUT top file, OV1_top are not ports, they are internal signals. Can i access them as inst_OV1_top.awg_rd_* ? In that case, its pretty straight forward.

IF NOT…

Can i just update the above bind for multiple instances as below?


genvar g;
 generate
   for (g = 0; g < 25; g++) begin 
     bind RAM: inst_OV1_TOP.inst_RAM_awg[g] dut_sig_if awg_if[g](.awg_rd_addr(awg_rd_addr), .awg_rd_data[g](awg_rd_data[g]));
   end
endgenerate

  initial begin
  :   for (i=0;i<25; i++) begin
        uvm_config_db#(virtual dut_sig_if)::set(null, "*","wave_gen_if[i]", inst_OV1_TOP.inst_RAM_awg[i].awg_if[i]); 
      end //for
  :
  end  //initial 

will awg_if be considered as array of interfaces of type dut_sig_if? Can i push it in uvm_config_db? Its bit confusing. How do i access it in monitor? Any pointer would help.
I came across below link:

Didn’t get everything there and also its mainly for assertions. Moreover, I have as many RAM instances in my case. So, not sure if similar thing applies here.

Thank you!

In reply to uvmsd:

Of course you can use an array of interfaces like this:

dut_sig_if if[24:0] ();

You can put it into the config_db as any other interface.

In reply to chr_sue:

Thanks Chris,
First i tried to access internal signals of OV1_top and realized that we can access only interface signals.
I tried something like below


//RTL TOP
module OV1_top;
  :
  :
      generate
        for (g = 0; g < 25; g++) begin 
          RAM inst_RAM_awg (
              .clk     (system_clk_spi),
              .wr      (awg_wr_en[g]),
              .wr_addr (awg_wr_addr),
              .wr_data (spi_dout[g]),
              .rd_addr (awg_rd_addr),
              .rd_data (awg_rd_data[g])
            );
        end
      endgenerate
  :
  :
endmodule
 

 module tb_OV1_top

   :
   :
OV1_TOP inst_OV1_top (
 :
 :
);
  :
  :
  genvar g;
// dut_sig_if awg_if[24:0];
   generate
     for(g = 0; g < NUM_GROUPS; g++) begin 
       bind RAM[g] : inst_OV1_TOP.inst_RAM_awg[g] dut_sig_if awg_if[g](
        .awg_rd_addr(awg_rd_addr), .awg_rd_data(awg_rd_data[g]));   
    end//for
  endgenerate

   initial begin
     :
     for (int i=0; i<NUM_GROUPS; i++) begin
       uvm_config_db#(virtual dut_sig_if)::set(null,"*","wave_gen_if[i]",inst_OV1_TOP.inst_RAM_awg[i].awg_if[i]);
      end // for
     :
    end //initial 

endmodule //TB top

Though things go through compilation, i am getting below load error:
** Error (suppressible): (vopt-2734) …/tb_ov1_top/tb_ov1_top.sv(138): Failed to find ‘inst_RAM_awg’ in hierarchical name inst_OV1_TOP.inst_RAM_awg[g].

and line 138 corresponds to below line in tb_OV1_top:
bind RAM[g] : inst_OV1_TOP.inst_RAM_awg[g] dut_sig_if awg_if[g](
.awg_rd_addr(awg_rd_addr), .awg_rd_data(awg_rd_data[g]));

Its trying to locate inst_RAM_awg in inst_OV1_TOP.inst_RAM_awg[g] ? I feel, thats the way to write bind statement. Where am i wrong?

In reply to uvmsd:

It looks like the path

inst_OV1_TOP.inst_RAM_awg[g]

is not existing.

I’m not sure if your bindd syntax is really correct. I did not see before what you are writing:

bind RAM[g] : inst_OV1_TOP ....

In my understanding the syntax is

bind design/inst design_to_be_bind inst_name(arg1, arg2, ...)

where design/inst is the design or instance you want to a module to
design_to_be_bind the module which has to be bind
inst_name instance name of the design_to_be_bind

In reply to chr_sue:

Thanks Chris,
I think, I came across below in some link

bind RAM[g] : inst_OV1_TOP.....

As per your above response, I modified my bind. Still had issues.
I might be messing up with the path for ‘design/inst’ the first field.
Should it be absolute path?

Eg:

module dut_top;
     :
     bit sig1;
     bit sig2;
     :
     sub_module1 inst_sub_module1(.sig1(sig1),.sig2(sig2)     
     );
     :
     end module : dut_top

interface my_if(input bit sig1, sig2);
:
endinterface

module tb_top
   :
   dut_top inst_dut_top(
    :
    );
   :  
  bind dut_top.sub_module1 my_if inst_my_if(.sig1(sig1), .sig2(sig2));
   initial begin
       uvm_config_db#(virtual my_if)::set(null,"*","sig1",inst_dut_top.inst_sub_module1.inst_my_if);
   end
endmodule

I am not sure about the path we mention for the submodule we mention in bind. Can you please correct me?

After trying many options of bind, just thought to access the internal signals of DUT top, as below:
In above case :

module tb_top;
  :
initial begin
  uvm_config_db#(bit)::set(null,"*","sig1",inst_dut_top.sig1);
  uvm_config_db#(bit)::set(null,"*","sig1",inst_dut_top.sig2);

end
endmodule

And in the monitor accessed the same using:


class my_mon extends uvm_monitor;
  bit sig_mon;
   :
 function void build_phase(uvm_phase phase);
    if(!uvm_config_db#(bit)::get(this, "", "sig1", sig_mon))
      `uvm_fatal("NO_VIF",{"virtual interface must be set for: ",get_full_name(),".sig_mon"});
  endfunction
 :
endclass 
``` verilog


Will above work? it did go through compilation/loading though.

In reply to uvmsd:

A few remarks:

(1) The path has to be the full path (absolute) starting from the DUT instance.
(2) You are passing the whole interface to the config_db like this

 initial begin
       uvm_config_db#(virtual my_if)::set(null,"*", "if1", inst_dut_top.inst_sub_module1.inst_my_if);
   end

(3) in the monitor you retrieve the whole interface from the config_db:
function void connect_phase(uvm_phase phase);

 if(!uvm_config_db#(virtual my_if)::get(this, "", "if1", mon_if1))
      `uvm_fatal("NO_VIF",{"virtual interface must be set for: ",get_full_name(),".sig_mon"});
  endfunction

To check the absolute path you can use

function int uvm_hdl_check_path(string  path)

This function returns 1’b1 if the path exists and 1’b0 if it does not.

In reply to chr_sue:

Thanks Chris for the clarification,

I wanted to know about the path that we mention in the bind.

 bind dut_top.sub_module1 my_if inst_my_if(.sig1(sig1), .sig2(sig2)); 

Does it have to be dut_top.sub_module1 or dut_top.inst_sub_module1.
But I tried all the options. It was throwing a load time error for the path.

Finally, thought of accessing the signals directly, by setting inst_dut_top.sig1 & sig2 in config_db and then getting them in monitor.
Eg: (no bind is used)

 uvm_config_db#(bit)::set(null,"*","sig1",inst_dut_top.sig1);
  uvm_config_db#(bit)::set(null,"*","sig1",inst_dut_top.sig2); 

Accessing them in the monitor as below:

 if(!uvm_config_db#(bit)::get(this, "", "sig1", sig1_mon))
      `uvm_fatal("NO_sig1",{"Sig1 must be set for: ",get_full_name(),".sig1_mon"}); 

In reply to uvmsd:

If you want to bind to a design, i.e. module then you do not need a hierarchy. You can directly bind to sub_module1. Only in case you want to bind to an instance you need the hierachical path.
If you have Verilog or SV design then you can use the hierarchical path directly. If you do this in multiple cases and you change your hierachy you have to adopt these pathes in all these places. This is not a smart solution, but it is legal.

In reply to chr_sue:

In reply to uvmsd:
If you want to bind to a design, i.e. module then you do not need a hierarchy. You can directly bind to sub_module1. Only in case you want to bind to an instance you need the hierachical path.

Wow, this is useful info to me. In my case, I had to refer to instance, since there were multiple instances of RAM in the dut top. Thanks for all the info.

As of now, i just have these two signals to be accessed on TB side. Will go ahead using the hierarchical path. I still have the code using bind. Will explore when i get chance.
Thank you Chris,

In reply to uvmsd:

It is a pleasure to me working with you.
Wish you more success.