How to use the arguments of two different sequences for comparing in a third sequence using Assertions?

I coded assertion like this but the simulator is showing this error

** Error: …/ref/i2c_assertions.sv(82): Illegal SVA expression in Verilog expression.

Can anyone point out what’s the mistake?





//START condition





//Detects the time where SCL went LOW

sequence scl_fall(Tscl_fall);

($fell(I2C_INTERFACE.scl),Tscl_fall=$time);
endsequence



//Detects where the time where SDA went LOW


sequence sda_fall(Tsda_fall);

($fell(I2C_INTERFACE.sda),Tsda_fall=$time);

endsequence


//Checking for START condition


sequence s1;

time t1,t2;

(((scl_fall(t1))>(sda_fall(t2))),$display("START condition dtected at %0t",sda_fall(t2)));
endsequence



property p1;

@(negedge I2C_INTERFACE.scl) s1 ;

endproperty

I explain the issue you have in my SVA Handbook; basically, typed formal arguments declared as local variables can be exported to the calling unit, such as the property or sequence, if they are prefixed with the reserved word local and are declared of direction inout or output. Variables declared in the assertion variable declaration region (i.e., those not in the port list) cannot be exported. Here is a link to my book that explains the rules in handling the exportation of local variable

Below is the sample code and simple testbench for it.


//1.  Data transfer from the external interface into the driver. 
//2.  Data transfer from the driver to the memory through another interface. 
//3.  Verification that data stored into the memory at that address location is identical to what was originally transferred from the originator of the data
module var_example;
    bit clk, ext1_req, ext1_ack, ext1_rd_wrf; 
    bit ext2_req, ext2_ack, ext2_rd_wrf; 
    byte ext1_data, ext2_data, drv_data;  
    byte mem[0:1024]; 
    bit[0:9] ext1_addr, ext2_addr; 
    initial forever #10 clk=!clk; 
    default clocking cb_clk @ (posedge clk);
    endclocking 
    sequence q_ext1_to_driver(local output byte v_data, 
        local output bit[0:9] v_addr);
        first_match (($rose(ext1_req) && !ext1_rd_wrf, v_data=ext1_data, 
        v_addr=ext1_addr)  ##[1:3] ext1_ack);
    endsequence : q_ext1_to_driver

    sequence q_ext2_to_mem(local input byte v_data, local input bit[0:9] v_addr);
        ext2_req ##0 !ext2_rd_wrf ##0 ext2_data==v_data ##0 ext2_addr==v_addr ##
        1 ext2_ack ##[1:3] mem[v_addr]==v_data; 
    endsequence

    property p_ext1_to_mem; 
        byte v_data; 
        bit[0:9] v_addr; 
        q_ext1_to_driver(v_data, v_addr) |-> 
        ##[1:3] q_ext2_to_mem(v_data, v_addr);
    endproperty : p_ext1_to_mem
    ap_ext1_to_mem: assert property(p_ext1_to_mem);

    always begin
        if(!randomize(ext1_data, ext1_addr))  $display("randomization failure");
        @ (posedge clk);
        ext1_req <= 1'b1; 
        ext1_rd_wrf <= 1'b0; 
        mem[ext1_addr] <= ext1_data; // preload of data for test only 
        @ (posedge clk); 
        ext1_req <= 1'b0; 
        ext1_rd_wrf <= 1'b1; 
        ext1_ack <= 1'b1; 
        @ (posedge clk);
        ext2_req <= 1'b1; 
        ext2_rd_wrf <= 1'b0; 
        ext2_data <= ext1_data;
        ext2_addr <= ext1_addr;
        ext1_ack <= 1'b0; 
        @ (posedge clk);
        ext2_req <= 1'b0; 
        ext2_rd_wrf <= 1'b1; 
        ext2_ack <= 1'b1; 
        @ (posedge clk); 
        ext2_ack <= 1'b0; 
        repeat (5) @ (posedge clk);
    end
endmodule : var_example    

Ben Cohen
http://www.systemverilog.us/ ben@systemverilog.us

  • SVA Handbook 4th Edition, 2016 ISBN 978-1518681448
  • A Pragmatic Approach to VMM Adoption 2006 ISBN 0-9705394-9-5
  • Using PSL/SUGAR for Formal and Dynamic Verification 2nd Edition, 2004, ISBN 0-9705394-6-0
  • Real Chip Design and Verification Using Verilog and VHDL, 2002 isbn 0-9705394-2-8
  • Component Design by Example ", 2001 ISBN 0-9705394-0-1
  • VHDL Coding Styles and Methodologies, 2nd Edition, 1999 ISBN 0-7923-8474-1
  • VHDL Answers to Frequently Asked Questions, 2nd Edition ISBN 0-7923-8115

In reply to ben@SystemVerilog.us:

Thanks for the reply Ben.Actually I am trying to write assertions for I2C protocol where I need to check the following scenarios.

1.START condition

When SCL is HIGH and before it goes LOW, SDA should go LOW.(Here SDA and SCL start from being HIGH initially)

2.The START condition is followed by checking of config class variable being 1 at posedge of SCL.

How to generate actual delays instead of Cycle delays?

I wrote a code like this will it work?If not please suggest me a better method sir.



 sequence s1;

	@(negedge I2C_INTERFACE.sda) (I2C_INTERFACE.scl==1);
endsequence

task posedge_delay();

	@(posedge I2C_INTERFACE.scl);

endtask

property p1;

	@(negedge I2C_INTERFACE.sda) s1;

endproperty





property p5;

	@ (posedge I2C_INTERFACE.scl)	s1|->(1'b1,posedge_delay)|->(slave_addr_tran==1'b1);


endproperty


In reply to rajan passionate:

Consider using the within sequence operator. The sequence containment within specifies a sequence occurring within another sequence.
Note: (seq1 within seq2) is equivalent to:
((1[*0:] ##1 seq1 ##1 1[*0:]) intersect seq2 )
You also seem to overuse the sequence declaration, and I see no use for your task.
I don’t know if this assertion will work for you, but I suggest something like.


// When SCL is HIGH and before it goes LOW, 
//   SDA should go LOW.(Here SDA and SCL start from being HIGH initially)
ap_scl: assert property(
  @ (posedge some_clock) $rose(scl)|-> !sda[->1] within $fell(scl)[->1]); 
// @ (posedge some_clock) $rose(scl)|-> !sda[->1] within ($fell(scl) && sda)[->1]);  
// A pootential other option.
// As far as "(1'b1,posedge_delay)|->(slave_addr_tran==1'b1)" 
// I have no idea as to what you are talking about. 
// If there is a relationship between $fell(sda) and slave_addr_tran, 
// write another assertion 

Ben Cohen
http://www.systemverilog.us/ ben@systemverilog.us

  • SVA Handbook 4th Edition, 2016 ISBN 978-1518681448
  • A Pragmatic Approach to VMM Adoption 2006 ISBN 0-9705394-9-5
  • Using PSL/SUGAR for Formal and Dynamic Verification 2nd Edition, 2004, ISBN 0-9705394-6-0
  • Real Chip Design and Verification Using Verilog and VHDL, 2002 isbn 0-9705394-2-8
  • Component Design by Example ", 2001 ISBN 0-9705394-0-1
  • VHDL Coding Styles and Methodologies, 2nd Edition, 1999 ISBN 0-7923-8474-1
  • VHDL Answers to Frequently Asked Questions, 2nd Edition ISBN 0-7923-8115

In reply to ben@SystemVerilog.us:

1.Here my clock itself is scl.So,I cant check the START condition if I use @(posedge scl) as my clocking event.So, I used @(negedge sda).

2.After START was detected,at the next immediate posedge of SCL,checking of cfg.slave_addr_tran ==1’b1 should be done.So, I thought of using a task which will produce delay of one posedge of scl.

In reply to rajan passionate:
Show the code. Did you compile it too?
Ben

In reply to ben@SystemVerilog.us:

I thought of using a task which will produce delay of one posedge of scl.

BTW, firing a task from a sequence_match_item just fires it, like a function call. There is no time consumption in the assertion.
It is discouraged to use tasks; I haven’t chhecked, but I believe it might be illegal. If legal, it does nothing for the assertion (no time consumption processed by the assertion as a result of the task call). You can use functions that consume no time.
Ben SystemVerilog.us

In reply to ben@SystemVerilog.us:


//`include "i2c_config.sv"
import CONFIG::*;
module i2c_assertions();


time Tsda_rise,Tscl_rise;

time Tscl_fall,Tsda_fall;
logic scl;
bit slave_addr_tran;
bit mem_addr_tran;
bit data_tran_WR;
bit data_tran_RD;

i2c_if I2C_INTERFACE(.scl(scl));

initial
begin
i2c_config cfg;
//cfg = new();
end
/*
always @(posedge I2C_INTERFACE.scl)
begin

	slave_addr_tran   =   cfg.slave_addr_tran;

	mem_addr_tran     =   cfg.mem_addr_tran;

	data_tran_WR  =   cfg.data_tran_WR;

	data_tran_RD  =   cfg.data_tran_RD;
end
*/
//Config class 



//*******************************************************PROPERTIES and SEQUENCES*****************************************************//


//typedef class i2c_config;// cfg;
//cfg = new();
/*
initial
begin
cfg = new();
end

*/
//START condition






//Detects START condition
sequence s1;

	@(negedge I2C_INTERFACE.sda) (I2C_INTERFACE.scl==1);

endsequence

task posedge_delay();

	@(posedge I2C_INTERFACE.scl);

endtask

property p1;

	@(negedge I2C_INTERFACE.sda) s1;

endproperty

//Detects STOP condition

sequence s2;

	@(posedge I2C_INTERFACE.sda) (I2C_INTERFACE.scl==1);

endsequence


property p2;

@(negedge I2C_INTERFACE.sda) s2;

endproperty







//BUS IDLE


/*

property p4;

@(I2C_INTERFACE.scl) s2|=>(I2C_INTERFACE.sda && I2C_INTERFACE.scl)|=>s1; 

endproperty

*/

//SLAVE ADDRESS TRANSFER & ACK




//SLAVE ADDRESS trasnfer starts


property p5;

	@ (posedge I2C_INTERFACE.scl)	s1|->(1'b1,posedge_delay)|->(slave_addr_tran==1'b1);


endproperty




//SLAVE ADDRESS transfer ends


property p6;

@(posedge I2C_INTERFACE.scl) s1|=> ##8 (slave_addr_tran==1'b0);

endproperty


//SLAVE ADDRESS ACK


property p7;

@(posedge I2C_INTERFACE.scl) s1|=> (slave_addr_tran==1'b1)|->##9(!(I2C_INTERFACE.sda_oe));

endproperty




//MEMORY ADDRESS TRANSFER & ACK





//MEMORY ADDRESS transfer starts



property p8;

@(posedge I2C_INTERFACE.scl) s1|=> (slave_addr_tran==1'b1)|->##9 !(I2C_INTERFACE.sda_oe)|=>(mem_addr_tran==1'b1);

endproperty




//MEMORY ADDRESS transfer ends


property p9;

@(posedge I2C_INTERFACE.scl) s1|=> (slave_addr_tran==1'b1)|->##9(!(I2C_INTERFACE.sda_oe))|=> ##8 (mem_addr_tran==1'b0);

endproperty



//MEMORY ADDRESS ACK




property p10;

@(posedge I2C_INTERFACE.scl) s1|=> (slave_addr_tran==1'b1)|->##9(!(I2C_INTERFACE.sda_oe))|=>##9 (!(I2C_INTERFACE.sda_oe));

endproperty





//DATA TRANSFER & ACK (WRITE operation)




//DATA TRANSFER starts



property p11;

@(posedge I2C_INTERFACE.scl) s1|=> (slave_addr_tran==1'b1)|->##9(!(I2C_INTERFACE.sda_oe))|=>##9 (!(I2C_INTERFACE.sda_oe))|=>(data_tran_WR==1'b1);


endproperty


//DATA TRANSFER ends

/*
property p12;

@(posedge I2C_INTERFACE.scl) s1|=> (cfg.slave_addr_tran_WR==1'b1)|->##9(!(I2C_INTERFACE.sda_oe))|=>##9 (!(I2C_INTERFACE.sda_oe))|=>##8 (cfg.data_tran_WR==1'b0);

endproperty
*/

//DATA TRANSFER ACK (by Slave)


property p13;

@(posedge I2C_INTERFACE.scl) s1|=> (slave_addr_tran==1'b1)|->##9(!(I2C_INTERFACE.sda_oe))|=>##9 (!(I2C_INTERFACE.sda_oe))|->##9 (!(I2C_INTERFACE.sda_oe));

endproperty


//DATA TRANSFER & ACK (READ operation)



//DATA TRANSFER starts


property p14;

@(posedge I2C_INTERFACE.scl) s1|=> (slave_addr_tran==1'b1)|->##9(!(I2C_INTERFACE.sda_oe))|=>##9 (!(I2C_INTERFACE.sda_oe))|=>(data_tran_RD==1'b1);


endproperty



//DATA TRANSFER ends

/*
property p15;

@(posedge I2C_INTERFACE.scl) s1|=> (cfg.slave_addr_tran==1'b1)|->##9(!(I2C_INTERFACE.sda_oe))|=>##9 (!(I2C_INTERFACE.sda_oe))|=>##8 (cfg.data_tran_RD==1'b0);

endproperty
*/

//DATA TRANSFER ACK (by Master)


property p16;

@(posedge I2C_INTERFACE.scl) s1|=> (slave_addr_tran==1'b1)|->##9(!(I2C_INTERFACE.sda_oe))|=>##9 (!(I2C_INTERFACE.sda_oe))|->##9 !(I2C_INTERFACE.sda);
 

endproperty



//RepStart condition



//When slave didn't respond during slave address transfer



property p17;

	@(posedge I2C_INTERFACE.scl) s1|=> (slave_addr_tran==1'b1)|=>##9 (I2C_INTERFACE.sda_oe);

endproperty


//When NACK sent during memory address transfer


property p18;

@(posedge I2C_INTERFACE.scl) s1|=> (mem_addr_tran==1'b1)|=>##9 (I2C_INTERFACE.sda_oe);

endproperty


//When NACK sent during data transfer

//WRITE to slave



property p19;

@(posedge I2C_INTERFACE.scl) s1|=> (data_tran_WR==1'b1)|=>##9 (I2C_INTERFACE.sda_oe);

endproperty



//READ from slave



property p20;

@(posedge I2C_INTERFACE.scl) s1|=> (data_tran_RD==1'b1)|=>##9 (I2C_INTERFACE.sda);

endproperty


//WRITE operation DETECT


property p21;

@(posedge I2C_INTERFACE.scl) s1|=> ##8(!(I2C_INTERFACE.sda));


endproperty


//READ operation detect


property p22;

@(posedge I2C_INTERFACE.scl) s1|=> ##8 I2C_INTERFACE.sda;

endproperty



//When slave didn't respond during slave address transfer


property p23;

	@(posedge I2C_INTERFACE.scl) s1|=> (slave_addr_tran==1'b1)|=>##9 (I2C_INTERFACE.sda_oe);


endproperty


//NACK sent by slave when master sends memory address


property p24;

@(posedge I2C_INTERFACE.scl) s1|=> (mem_addr_tran==1'b1)|=>##9 (I2C_INTERFACE.sda_oe);

endproperty


//NACK sent by slave after sending data to slave



property p25;

@(posedge I2C_INTERFACE.scl) s1|=> (data_tran_WR==1'b1)|=>##9 (I2C_INTERFACE.sda_oe);

endproperty



//NACK sent by master after recieving data from slave


property p26;

@(posedge I2C_INTERFACE.scl) s1|=> (data_tran_RD==1'b1)|=>##9 (I2C_INTERFACE.sda);

endproperty



//Illegal Format (START followed by STOP in the next cycle)





property p27;

@(posedge I2C_INTERFACE.scl) s1 |=> s2;

endproperty




















/////////////*****************************************************************ASSERTIONS*******************************************************************/////////////



START      :  assert property (p1) $display (" START condition detected at %0t ",$time); else $display("START NOT DETECTED")  ;



STOP       :  assert property (p2) $display (" STOP condition detected at %0t ",$time);


//VALID_DATA :  assert property (p3) $display (" INVALID SDA TRANSITION ");          else    $display("Assertion Failed : INVALID DATA");


//BUS_IDLE   :  assert property (p4) $display (" I2C bus went to IDLE state ");


SLAVE_ADDR_TRANSFER_START      :     assert property (p5)  $display (" SLAVE ADDRESS transfer started "); 

SLAVE_ADDR_TRANSFER_END        :     assert property (p6)  $display (" SLAVE ADDRESS transfer ended ");      


SLAVE_ADDR_ACK                 :     assert property (p7)  $display (" SLAVE generated ACK ");               


NO_RESPONSE_FROM_SLAVE         :     assert property (p23) $display (" SLAVE not responded for the slave address sent");    
                                                                                                    
                                                                                                   


MEM_ADDR_TRANSFER_START        :     assert property (p8)  $display (" MEMORY ADDRESS transfer started ");   

 
MEM_ADDR_TRANSFER_END          :     assert property (p9)  $display (" MEMORY ADDRESS transfer ended ");     


WRITE_TO_SLAVE_DETECT          :     assert property (p21) $display (" MASTER WRITES TO SLAVE ");           


READ_FROM_SLAVE_DETECT         :     assert property (p22) $display (" MASTER READS FROM SLAVE ");           


MEM_ADDR_ACK                   :     assert property (p10) $display (" SLAVE generated ACK after memory address has been sent by master ");

                                                                                                   


MEM_ADDR_NACK                  :     assert property (p24) $display (" SLAVE generated NACK after memory address has been sent by master ");

                                                                                                   

WRITE_DATA_TRANSFER_START      :     assert property (p11) $display (" DATA TRANSFER STARTED "); 
            

//WRITE_DATA_TRANSFER_END        :     assert property (p12) $display (" DATA TRANSFER ENDED ");               


DATA_ACK_BY_SLAVE              :     assert property (p13) $display (" SLAVE generated ACK ");              


DATA_NACK_BY_SLAVE             :     assert property (p25) $display (" SLAVE generated NACK after data has been sent by master ");   

                                                                                                   


READ_DATA_TRANSFER_START       :     assert property (p14) $display (" DATA TRANSFER STARTED ");            


//READ_DATA_TRANSFER_END         :     assert property (p15) $display (" DATA TRANSFER ENDED ");              


DATA_ACK_BY_MASTER             :     assert property (p16) $display (" MASTER generated ACK after data has been sent by SLAVE  ");                                     

                                                                                                   


DATA_NACK_BY_MASTER            :     assert property (p26) $display (" MASTER generated NACK after data has been sent by SLAVE ");

                                                                                                   


REP_START_1                    :     assert property (p17) $display (" RPEATED START DETECTED (No response to the SLAVE ADDRESS sent) ");   

                                                                                                   


REP_START_2                    :     assert property (p18) $display (" REPEATED START DETECTED (SLAVE generated NACK after the MEMORY ADDRESS  has been sent by the MASTER) ");

                                                                                                   

REP_START_3                    :     assert property (p19) $display (" REPEATED START DETECTED (SLAVE generated NACK after the DATA  has been sent by the  MASTER) ");

                                                                                                   


REP_START_4                    :     assert property (p20) $display (" REPEATED START DETECTED (MASTER generated NACK after the DATA has been sent by the slave) ");

                                                                                                   

ILLEGAL_FORMAT                 :     assert property (p27) $display (" ILLGEAL FORMAT:START followed by STOP in the next cycle ");





endmodule

In reply to rajan passionate:

Yes Ben, it is compiling and simulating too.But as the DUT has some bug all are vacuosly passed!!

In reply to rajan passionate:
I don’t have the time to debug it for you, as it is too specific.
Some general comments though about your style:

  • You use too many sequence declarations
  • Don’t fire tasks from sequence_match_items; they consume no time in the assertion,
  • For sequence_match_items, you can use functions.
  • If you are using the properties once, and do not use local variables avoid using property declarations; they make the code harder to follow.

Ben http://SystemVerilog.us

In reply to ben@SystemVerilog.us:

But ben, how to use non-clocked delays for checking the sequences? (Delays less than one clock cycle).

In reply to rajan passionate:

But ben, how to use non-clocked delays for checking the sequences? (Delays less than one clock cycle).

SVA is event-based with variables samples in the Preponed region of the event.
Events are usually clocking event, but could be user-defined events (declared with the event type).
Assertions are really intended to verify and cover properties based on the relationships of variables (e.g, req |=> ack)
For delays, you could use supporting logic yo create events.
Write multiple small assertions instead of 1 compex one.
Ben

In reply to ben@SystemVerilog.us:

Ok Ben.How can I do like that in my case?

It is quite complicated here.Because,
START condition has to be checked first when scl is HIGH.Then scl should go LOW and after some time it should go HIGH and at this posedge of SCL I need to sample a variable.

The problem occurs due clocking event.Because for START condition ,the clocking event is negedge of sda and for sampling the variable(cfg.slave_addr_tran) the clocking event is posedge scl.

Please suggest some solution.

In reply to rajan passionate:
General comments:

  • SVA can use multiclocks,
    @(posedge clk1) a |-> @(posedge clk2) b
  • Solve your particular verification with plain SystemVerilog code
    e.g.,
always   @(posedge clk)  begin
...
end

On this forum, I generally provide guidance and quick help. However, for detailed complex problems, I really do not have the time, and besides, it is not my responsibility anyway.
Best wishes.
Ben