Regarding assertion

Hi,
I am making an environment in UVM to check the functionality of FIFO. In order to do this in driver class I have defined write and read tasks and also defined a variable called “count” as static. The purpose of this variable to track the size of the fifo and check it’s output signals fifo_ful and fifo_empty. For example if fifo size = 10 , then whenever write function is called count gets incremented by 1 and whenever read function is called count gets decremented by 1. Now I have defined two properties in interface like this :

fifo_driver drv_count;

property check_for_fifofull;
@(posedge WCLK) (drv_count.count == `FIFO_SIZE) |=> FIFO_FULL ;
endproperty

property check_for_fifoempty;
@(posedge RCLK) (drv_count.count == 0 ) |=> FIFO_EMPTY ;

endproperty

check_full : assert property (check_for_fifofull)
else
`uvm_error(“CHECK FOR FIFO FULL FAILED”,“fifo”)

check_empty : assert property (check_for_fifoempty)
else
`uvm_error(“CHECK FOR FIFO EMPTY FAILED”,“fifo”)

I am getting the following error while compilation.

Error-[ETTNATE] Invalid type in temporal expression
/proj/ams_ddrphy_funcv/users/maitri/lpddr4_24th_sep/verif/fifo/apb_interface.sv, 30
check_full, “drv_count.count”
Expressions involving real, string, event and dynamic SystemVerilog types
are not allowed in Boolean expressions in properties and sequences.

can I write an assertion like this ? Can I access the member of the class from property ?

Thanks,
Maitri

In reply to Maitri@07:

The question is which is

drv_count

?

Is it struct or is it a class?

In reply to chr_sue:

Hi,
fifo_driver is a driver class. drv_count is an object of this driver class.

In reply to Maitri@07:

If drv_count is a class object then you cannot write the assertion as you did. The error message points you to this situation:
‘Expressions involving real, string, event and dynamic SystemVerilog types
are not allowed in Boolean expressions in properties and sequences.’
A class type is a dynamic type.

In reply to chr_sue:

As per this SVA in a UVM Class-based Environment | Verification Horizons | Verification Academy

. Assertions can also access static variables defined in classes; however, access to dynamic or rand variables is illegal.

I have defined count as static in the driver class.

In reply to Maitri@07:

Could you please show the code of class driver.

In reply to chr_sue:

class fifo_driver extends uvm_driver # (amd_apb_transaction);

typedef virtual fifo_interface fifo_vif;
fifo_vif vif;
static int count = 0;
extern function new(string name = “fifo_driver”, uvm_component parent = null);
extern virtual function void connect_phase(uvm_phase phase);
extern virtual task run_phase(uvm_phase phase);
extern virtual protected task wclk_gen();
extern virtual protected task rclk_gen();
extern virtual protected task get_and_drive();
extern virtual protected task write(input bit [3:0] data);
extern virtual protected task read(output logic [3:0] data);
endclass: fifo_driver

task fifo_driver::run_phase(uvm_phase phase);
super.run_phase(phase);
fork
this.get_and_drive();
this.wclk_gen();
this.rclk_gen();
join
endtask: run_phase
// ---------------------- clock generation ---------------------- //
task fifo_driver::wclk_gen();

bit clk;
this.pclk_en = 1;
this.vif.WCLK = 0;

forever begin
if (this.pclk_en == 1)begin
#(PCLK_PERIOD/2); clk = ~clk; this.vif.WCLK = clk; end else begin uvm_info($sformatf(“%s:CLK_GEN”,get_name()), “STOPPING WPCLK”, UVM_LOW)
break;
end
end

endtask: wclk_gen

// ---------------------- DFI clock generation ---------------------- //
task fifo_driver::rclk_gen();

bit dficlk=0;
this.dficlk_en = 1;
this.vif.RCLK = 0;

forever begin
if (this.dficlk_en == 1)begin
#(DFI_CLK_PERIOD/2); dficlk = ~dficlk; this.vif.RCLK = dficlk; end else begin uvm_info($sformatf(“%s:CLK_GEN”,get_name()), “STOPPING DFICLK”, UVM_LOW)
break;
end
end

endtask: rclk_gen

// ---------------------- get_and_drive ---------------------- //
task fifo_driver::get_and_drive();

`uvm_info($sformatf(“%s:get_and_drive”,get_name()), “STARTING get_and_drive”, UVM_LOW)

forever begin
fifo_transaction tr;
seq_item_port.get_next_item(tr);

  fork
  case (tr.kind)
    fifo_transaction::read        : this.read( tr.rdata);  
    fifo_transaction::write       : this.write(tr.wdata);
  endcase
join_any
disable fork;
seq_item_port.item_done();

end //forever begin
`uvm_info($sformatf(“%s:get_and_drive”,get_name()), “STOPPING get_and_drive”, UVM_LOW)
endtask: get_and_drive

// ---------------------- Read operation ---------------------- //
static task fifo_driver::read(output logic [3:0] data);

this.vif.RE = 1’b1;
@(posedge this.vif.RCLK);
if (count != 1’b0) begin
data = this.vif.PRDATA;
count = count - 1;
$display(“read count %d time %t”,count,$time);

end
else
begin
$display(“count in else part %d time %t”,count,$time);
end
this.vif.RE = 1’b0;
endtask: read
//
//// ---------------------- Write operation ---------------------- //
static task fifo_driver::write(input bit [3:0] data);

this.vif.WE = 1’b1;
@(posedge this.vif.WCLK);
if (count <= `FIFO_SIZE) begin
this.vif.PWDATA = data;
count = count + 1;
$display(“write count %d time %t”,count,$time);
//this.vif.fifo_full = 1’b0;
end
else
begin
$display(“count in else part %d”,count);
end
this.vif.WE = 1’b0;
endtask: write

In reply to Maitri@07:

Thanks for providing your code. It looks like you are facing a tool issue. I could compile your code with some minor modifications using Questa 10.6b.

In reply to chr_sue:

Okay . Thanks you so much for your time. So my way of using the assertion is correct. Right ? I also wanted to ask you that in my code I am verifying for 1) write only
2) read only cases

Basically I am writing continuously for 10 times and then reading continuously . What if I want to do both the operations(wr/rd) simultaneously ? Do I need 2 agents ?

In reply to Maitri@07:

Sorry I have to correct me. The code does not contain the assertions.
Give me a few hours to look to the whole story.

In reply to Maitri@07:

In reply to chr_sue:
Okay . Thanks you so much for your time. So my way of using the assertion is correct. Right ? I also wanted to ask you that in my code I am verifying for 1) write only

  1. read only cases
    Basically I am writing continuously for 10 times and then reading continuously . What if I want to do both the operations(wr/rd) simultaneously ? Do I need 2 agents ?

The driver object in the interface where you are implementing the assertions makes your life complicated. I’d avoid such a combination, adding simply an additional signal count to the fifo_interface and assigning this in the driver object.
But the best solution would be to have all your properties/assertions in a seperate ‘assertion module’ you can connect to your testbenc or DUT using the bind construct.

WRT to your question about the numbers of agents. I assume your DUT is not able to write and to read at exactly the same time. What you are cuttently doing is running WR and RD in parallel using a fork/join. But this selects always 1 operation followed by the next one.

In reply to chr_sue:

And here is the complete code, compiling in Questa without errors:

import uvm_pkg::*;
`include "uvm_macros.svh"

typedef class fifo_driver;


interface fifo_interface (input logic WCLK, input logic RCLK); 

  logic PSEL, PENABLE, PWRITE;
  logic [15:0] PADDR;
  logic [31:0] PWDATA;
  logic [31:0] PRDATA;

  bit full;

fifo_driver drv_count;

property check_for_fifofull;
@(posedge WCLK) (drv_count.count == 20) |=> full ;
endproperty

property check_for_fifoempty;
@(posedge RCLK) (drv_count.count == 0 ) |=> !full ;

endproperty

check_full : assert property (check_for_fifofull)
else
`uvm_error("CHECK FOR FIFO FULL FAILED","fifo")

check_empty : assert property (check_for_fifoempty)
else
`uvm_error("CHECK FOR FIFO EMPTY FAILED","fifo")

endinterface

class fifo_transaction extends uvm_object;
   int rdata;
   int wdata;
   enum {read, write}  kind;

   function new(string name = "");
     super.new(name);
   endfunction  
endclass

class amd_apb_transaction extends uvm_object;

   function new(string name = "");
     super.new(name);
   endfunction  
endclass

class fifo_driver extends uvm_driver # (amd_apb_transaction);

typedef virtual fifo_interface fifo_vif; 
fifo_vif vif;
static int count = 0;
extern function new(string name = "fifo_driver", uvm_component parent = null); 
//extern virtual function void connect_phase(uvm_phase phase);
extern virtual task run_phase(uvm_phase phase);
extern virtual protected task wclk_gen();
extern virtual protected task rclk_gen();
extern virtual protected task get_and_drive();
extern virtual protected task write(input bit [3:0] data);
extern virtual protected task read(output logic [3:0] data);
endclass: fifo_driver
task fifo_driver::run_phase(uvm_phase phase);
super.run_phase(phase);
fork
this.get_and_drive();
this.wclk_gen();
this.rclk_gen();
join 
endtask: run_phase
// ---------------------- clock generation ---------------------- //
task fifo_driver::wclk_gen();
bit clk;
this.pclk_en = 1;
this.vif.WCLK = 0; 
forever begin
if (this.pclk_en == 1)begin
#50;
clk = ~clk;
this.vif.WCLK = clk;
end 
else begin
`uvm_info($sformatf("%s:CLK_GEN",get_name()), "STOPPING WPCLK", UVM_LOW)
break;
end
end
endtask: wclk_gen
// ---------------------- DFI clock generation ---------------------- //
task fifo_driver::rclk_gen();
bit dficlk=0;
this.dficlk_en = 1;
this.vif.RCLK = 0;
forever begin
if (this.dficlk_en == 1)begin
#25;
dficlk = ~dficlk;
this.vif.RCLK = dficlk;
end 
else begin
`uvm_info($sformatf("%s:CLK_GEN",get_name()), "STOPPING DFICLK", UVM_LOW)
break;
end
end
endtask: rclk_gen
// ---------------------- get_and_drive ---------------------- //
function fifo_driver::new(string name = "fifo_driver", uvm_component parent = null);
   super.new(name, parent);
endfunction

task fifo_driver::get_and_drive();
`uvm_info($sformatf("%s:get_and_drive",get_name()), "STARTING get_and_drive", UVM_LOW)

forever begin
fifo_transaction tr;
seq_item_port.get_next_item(tr);
fork
case (tr.kind)
fifo_transaction::read : this.read( tr.rdata); 
fifo_transaction::write : this.write(tr.wdata);
endcase
join_any
disable fork;
seq_item_port.item_done();
end //forever begin
`uvm_info($sformatf("%s:get_and_drive",get_name()), "STOPPING get_and_drive", UVM_LOW)
endtask: get_and_drive
// ---------------------- Read operation ---------------------- //
task fifo_driver::read(output logic [3:0] data);

this.vif.RE = 1'b1;
@(posedge this.vif.RCLK);
if (count != 1'b0) begin
data = this.vif.PRDATA;
count = count - 1;
$display("read count %d time %t",count,$time);

end 
else 
begin
$display("count in else part %d time %t",count,$time);
end 
this.vif.RE = 1'b0; 
endtask: read
//
//// ---------------------- Write operation ---------------------- //
task fifo_driver::write(input bit [3:0] data);

this.vif.WE = 1'b1;
@(posedge this.vif.WCLK);
if (count <= 20) begin
this.vif.PWDATA = data;
count = count + 1;
$display("write count %d time %t",count,$time);
//this.vif.fifo_full = 1'b0;
end 
else 
begin
$display("count in else part %d",count); 
end 
this.vif.WE = 1'b0;
endtask: write

In reply to chr_sue:

Hi ,
I was trying to write the code for the scenario of simultaneous write and read . As per your suggestion I instantiated the same agent and added an extra uvm_sequence. So I have two uvm_sequence , one is driving on one agent which is having sequential write and read operations[writing 10 data and then reading them back] , second is driving on second agent which contains simultaneous write and read operations.
repeat(10) begin
fork
uvm_do_with(req, {kind == amd_apb_transaction::write;}); uvm_do_with(req, {kind == amd_apb_transaction::read;});
join
end

In this manner what I am seeing in the waveform that it’s not doing write and read simultaneously. It’s writing first and then reading. Is my understanding correct here ? What can possibly go wrong here ?

In reply to chr_sue:

In reply to chr_sue:
And here is the complete code, compiling in Questa without errors:

import uvm_pkg::*;
`include "uvm_macros.svh"
typedef class fifo_driver;
interface fifo_interface (input logic WCLK, input logic RCLK); 
logic PSEL, PENABLE, PWRITE;
logic [15:0] PADDR;
logic [31:0] PWDATA;
logic [31:0] PRDATA;
bit full;
fifo_driver drv_count;
property check_for_fifofull;
@(posedge WCLK) (drv_count.count == 20) |=> full ;
endproperty
property check_for_fifoempty;
@(posedge RCLK) (drv_count.count == 0 ) |=> !full ;
endproperty
check_full : assert property (check_for_fifofull)
else
`uvm_error("CHECK FOR FIFO FULL FAILED","fifo")
check_empty : assert property (check_for_fifoempty)
else
`uvm_error("CHECK FOR FIFO EMPTY FAILED","fifo")
endinterface
class fifo_transaction extends uvm_object;
int rdata;
int wdata;
enum {read, write}  kind;
function new(string name = "");
super.new(name);
endfunction  
endclass
class amd_apb_transaction extends uvm_object;
function new(string name = "");
super.new(name);
endfunction  
endclass
class fifo_driver extends uvm_driver # (amd_apb_transaction);
typedef virtual fifo_interface fifo_vif; 
fifo_vif vif;
static int count = 0;
extern function new(string name = "fifo_driver", uvm_component parent = null); 
//extern virtual function void connect_phase(uvm_phase phase);
extern virtual task run_phase(uvm_phase phase);
extern virtual protected task wclk_gen();
extern virtual protected task rclk_gen();
extern virtual protected task get_and_drive();
extern virtual protected task write(input bit [3:0] data);
extern virtual protected task read(output logic [3:0] data);
endclass: fifo_driver
task fifo_driver::run_phase(uvm_phase phase);
super.run_phase(phase);
fork
this.get_and_drive();
this.wclk_gen();
this.rclk_gen();
join 
endtask: run_phase
// ---------------------- clock generation ---------------------- //
task fifo_driver::wclk_gen();
bit clk;
this.pclk_en = 1;
this.vif.WCLK = 0; 
forever begin
if (this.pclk_en == 1)begin
#50;
clk = ~clk;
this.vif.WCLK = clk;
end 
else begin
`uvm_info($sformatf("%s:CLK_GEN",get_name()), "STOPPING WPCLK", UVM_LOW)
break;
end
end
endtask: wclk_gen
// ---------------------- DFI clock generation ---------------------- //
task fifo_driver::rclk_gen();
bit dficlk=0;
this.dficlk_en = 1;
this.vif.RCLK = 0;
forever begin
if (this.dficlk_en == 1)begin
#25;
dficlk = ~dficlk;
this.vif.RCLK = dficlk;
end 
else begin
`uvm_info($sformatf("%s:CLK_GEN",get_name()), "STOPPING DFICLK", UVM_LOW)
break;
end
end
endtask: rclk_gen
// ---------------------- get_and_drive ---------------------- //
function fifo_driver::new(string name = "fifo_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
task fifo_driver::get_and_drive();
`uvm_info($sformatf("%s:get_and_drive",get_name()), "STARTING get_and_drive", UVM_LOW)
forever begin
fifo_transaction tr;
seq_item_port.get_next_item(tr);
fork
case (tr.kind)
fifo_transaction::read : this.read( tr.rdata); 
fifo_transaction::write : this.write(tr.wdata);
endcase
join_any
disable fork;
seq_item_port.item_done();
end //forever begin
`uvm_info($sformatf("%s:get_and_drive",get_name()), "STOPPING get_and_drive", UVM_LOW)
endtask: get_and_drive
// ---------------------- Read operation ---------------------- //
task fifo_driver::read(output logic [3:0] data);
this.vif.RE = 1'b1;
@(posedge this.vif.RCLK);
if (count != 1'b0) begin
data = this.vif.PRDATA;
count = count - 1;
$display("read count %d time %t",count,$time);
end 
else 
begin
$display("count in else part %d time %t",count,$time);
end 
this.vif.RE = 1'b0; 
endtask: read
//
//// ---------------------- Write operation ---------------------- //
task fifo_driver::write(input bit [3:0] data);
this.vif.WE = 1'b1;
@(posedge this.vif.WCLK);
if (count <= 20) begin
this.vif.PWDATA = data;
count = count + 1;
$display("write count %d time %t",count,$time);
//this.vif.fifo_full = 1'b0;
end 
else 
begin
$display("count in else part %d",count); 
end 
this.vif.WE = 1'b0;
endtask: write

Here I am not using Qesta sim so I won’t be able to check with this method . I will have to use scoreboard only

In reply to Maitri@07:

I think I will have to use the virtual sequence here to control two sequences on two agents. let me try this and I will get back to you.

In reply to Maitri@07:

In reply to chr_sue:
Hi ,
I was trying to write the code for the scenario of simultaneous write and read . As per your suggestion I instantiated the same agent and added an extra uvm_sequence. So I have two uvm_sequence , one is driving on one agent which is having sequential write and read operations[writing 10 data and then reading them back] , second is driving on second agent which contains simultaneous write and read operations.
repeat(10) begin
fork
uvm_do_with(req, {kind == amd_apb_transaction::write;}); uvm_do_with(req, {kind == amd_apb_transaction::read;});
join
end
In this manner what I am seeing in the waveform that it’s not doing write and read simultaneously. It’s writing first and then reading. Is my understanding correct here ? What can possibly go wrong here ?

If you are using fork/join you get interleaved accesses, staring randomly with 1 of your fork/join tasks.

In reply to chr_sue:

Yes . Can you tell me how can I achieve simultaneous behavior ?

In reply to Maitri@07:
If you want to have 2 seperate tasks running in parallel you need 2 agents.

In reply to chr_sue:

I am using 2 agents only. Like you suggested before ,I have instantiated the agent. One agent is writing and another agent is reading. In the test class in it’s run_phase I have run two sequences write and read on their respective agent’s sequencer.

task ddrphy_simple_read_write_test::run_phase(uvm_phase phase);
phase.raise_objection(this);
wr_h = write_seq::type_id::create(“wr_h”);
wr_h.start(env.write_agent.apb_sqr);

   rd_h = rd_seq::type_id::create("rd_h");
   rd_h.start(env.read_agent.apb_sqr);

   #50000;

   phase.drop_objection(this);
   //$finish;

endtask

How to make two agents work simultaneously ?