Wednesday, 17 September 2014

Timescale and Precision in Verilog & System Verilog

General description of timescale is "time allowed for or taken by a process or sequence of event." We can say System Verilog follows the same description of the timescale.

Let's see this from Verilog or System Verilog perspective. First of all `timescale is compiler directive. This compiler directive specifies the time unit and time precision of the design for particular module. Time unit is the unit of measurement for time values for simulation time as well as delay value.

Here we have used two words
simulation time : In one simulation time all event scheduling regions  specified in System verilog LRM file (IEEE std 1800-2012) is simulation time.
Delay value: Delay value specify the time that should be waited before moving forward at some point. For example you want to give 5ns delay before toggling clock signal value.

Time scale compiler directives is declared as follows :

   `timescale time_unit/time_precision

time_unit specifies the time unit of measurement during simulation for your design or test-bench.
time_precision specifies precision of time for time unit. Or in other way minimum time you are able to specify during simulation.

suppose you have specified timescale as follows.

                 `timescale 1 ns / 1 ps

This means in your System Verilog code whenever you specify #1 delay it will be considered as 1 ns delay.And one more thing you can specify the delay of minimum 1 ps = 0.001 ns. So your specified time delay in your design is rounded around 0.001 precision.

Following is some example that indicates how value of the delays are rounded.

         1.1           ns          is considered as    1.1       ns
         1.1234     ns          is considered as    1.123   ns
         1.1119     ns          is considered as    1.112    ns
         1.1123     ns          is considered as     1.112    ns

You can see that whatever the precision you specify your delay will get rounded around that value only. Neared time delay of 1.1113 in term of ps is 1.112 ns so it is rounded to that value.

Same way we have specified some timescale directives as follows.

`timescale 10ns / 1ns

In this timescale #2 will end up in 20 ns of delay. And you will be able to specify minimum 1ns of delay so #0.1 will give you 1ns of delay.

`timescale 0.001 ns / 1ps 

We can specify same timescale as  : `timescale 1ps / 1ps. 

Effect of Timescale

We have seen how to specify timescale. But what will be effect of the time scale ?? It is really important question as sometimes it happens that you spend 2 hours on debugging something event detection and after that you successfully find out that You have used wrong timescale for your module or package.

It is quite possible that in your top module (which is normally named as top ) you have specified timescale as 1ns / 1ns and in your design you are writing some delay like #25ps. In this case your compiler will count this as #0 ns only and you will not get this delay. That will end up in unspecified behavior of your design or System Verilog testbench.

It is also possible that you miss some pulse detection if it last for less time than your design or System Verilog testbench resolution. For example you have specified timescale as follows.

   `timescale 100ns / 10ns 

And you are getting some signal which is driven by other design which has different time scale with maximum value of resolution 1ns. This signal gives pulse signal which lasts for only one nano second only. So for our design it is quit possible that we miss that pulse and will end up in debugging everywhere in our design or System Verilog testbench.






Monday, 15 September 2014

Defining sequece for Assertion in System Verilog

Sequences works on clock ticks. This clock is define as events. In this post we will discuss something that would describe how to write sequential behaviour.

       a ##N b
- when a is true after exactly Nth clock ticks if b is true than sequence evaluates to true. Or we can say after delay of N clock b must be true.

Concatenate any sequence

 

(a ##1 b ##1 c) ##0 (d ##1 e ##1 f)
This seq indicates that once a is true after one clock cycle b is true and after one clock cycle c is true and at the same clock cycle second cycle starts and for that d should be true so ultimately  we can write the whole sequence as follows.
(a ##1 b ##1 c&d ##1 e ##1 f)


Up till now we have seen only linear sequence in which is written simply by specifying exact clock ticks between two events. But what if we have some cases when when delay is not fixed but finite clock ticks. Suppose we want to write sequence in which req is asserted at clock tick nth clock tick and after that in minimum 1 and maximum 5 clock ticks grant is being asserted. One way of doing this is write sequence as follows :
           (a ##1 b) or  (a ##2 b) or  (a ##3 b) or  (a ##4 b) or  (a ##5 b)
But what is there is delay of 5 to 100 clock ticks. If we write this 96 times that is completely absurd. In sequence we have [* repetition operator. The above sequence can be written as follows.
         a ##[1:5] b
Same way if you want to specify min_clk_ticks and max_clk_tiks it can be done as follows.
         a ##[min_clk_ticks:max_clk_ticks] b
If you want to specify min_clk_ticks to any clock tick before end of simulation it is specified by operator $. And expression for this as follows.
         a ##[min_clk_ticks:$] b
This expression indicates that when a is true b must be true any clock ticks after Nth clock ticks.


Goto repetition  [-> & Non-consecutive repetition  [=

Suppose we want to check that particular condition a should remain true for 10 consecutive clock cycle and after that b remains true for consecutive 2 clock cycle and that c is triggered to true. This sequence can be written as follows.
  a ##1 a ##1 a ##1 a ##1 a ##1 a ##1 a ##1 a ##1 a ##1 a ##1 a ##1 a ##1 a ##1 b ##1 b ##1 b ##1 c
But this is not comfortable if we are dealing with 100 clock ticks or 200 clock ticks. Right??
For this System Verilog provides us repetition operator for consecutive matches [* . The above sequence can be written as follows.
                  a ##[*10]  ##1 b ##[*3] ##1 c
We can specify the range of the repetition same way we have seen in previous post. For example :
                  b ##1 a[*0:2] ##2 c
This sequence indicates once b is true a should be true for either 0 times or 1 time  or  2 times and once a is true c should be true after two clock cycles. So, above sequence can be written as follows.
                  (b ##2 c) or (b ##1 a ##2 c) or (b ##2 a ##c)


Using repetition operator we can repeat some particular sequences also and combine it to trigger one seq. For example,
                              (a ##2 b) [*5]
This sequence is  same as
                     (a ##2 b ##1 a ##2 b ##1 a ##2 b ##1 a ##2 b ##1 a ##2 b)


To understand goto repetition we would consider simple example as follows. Let’s take an example of the following seq,
        a ##1 b [->4] ##1 c
This sequence indicates that after a is true b must be true four times with no gap between b and c. So this sequence says that when b is true 4th time it should be penultimate clock cycle of c is true.
         a ##1 ((!b[*0:$] ##1 b) [*4]) ##1 c


Non-consecutive repetition is same as goto repetition. The only difference is that it is not compulsory to be it in penultimate clock cycle.
             a ##1 b [=>4] ##1 c
This sequence indicates that after a is true b must be true four times with gap or no-gap between b and c. So this sequence says that when b is true 4th time it not necessary to be on penultimate clock cycle of c is true.
              a ##1 ((!b[*0:$] ##1 b) [*4]) ##1 !b[*0:$]  ##1 c

Saturday, 13 September 2014

Mailbox in System Verilog

Mailbox is the most common tool to for the communication between two class or processes while writing System Verilog test bench.
Conceptually maibox behaves like a real mailboxes that we use in our day to day life !! When latter is received in mailbox the person or owner of that mailbox can retrieve that mailbox if latter is not received person waits for latter. Same way mailbox works in System Verilog. System Verilog provides methods in mailbox to transfer controlled data between the process or different class.

Mailbox is nothing but a class. So there are some functions to manipulate queue of our object in mailbox like. put(), get(), new(), num(), try_put(), try_get() etc.
Mailboxes are created for either bounded or unbounded size of the queue. By default mailbox are created for unbounded size.  For bounded size when mailbox is full process can not put another data in mailbox. While for unbounded mailbox there is no such limits.

An example of creating a mailbox is as follows.
                  maibox mbx;
Mailbox is inbuilt class in system verilog that provides following methods for manipulation.

new()

Mailboxes are created using new() method.
                 prototype : function new(int bount = 0);
This new function returns handle of the mailbox. Input argument indicates the size of the mailbox user wants to keep in case if it is bound mailbox. Default value of this argument is 0. which indicated infinity size of the mailbox(Not practically !! Depends on RAM fo your machine). Bound value must be positive. Negative value of the bound will cause indefinite behavior of the mailbox.

num()

Number function of the mailbox gives total number of messages in mailbox.
  Prototype  : function int num();

put()

This method puts message in mailbox();
   Prototype : task put(singular message);
If mailbox is bounded size and if full than put() task is suspended till there is enough space in the mailbox. 

try_put();

This method puts message in mailbox(); but it is non-blocking method. 
  Prototype : function int try_put(singular message);
If mailbox is of bounded size and full than this function dose not put message mailbox and return without doing anything. If message has been put successfully in mailbox this method returns 0 otherwise it returns 1.

get()

This method fetches message from mailbox(). 
Prototype : task get(singular message);
If mailbox is empty this task suspends the process until some other process puts message in mailbox. This behavior is independent of size of the mailbox.

try_get()

This function fetches message from mailbox ; but it is not blocking process. 
Prototype : function int try_get(singular message);
If maibox is of bounded size and empty than this function does not retrieve any data from mailbox. If mailbox is empty this function returns 0 as it is not able to fetch any data. Other wise this function returns 1. 

peek()

This function does not remove data from mailbox queue it just copies the first data in mailbox queue.
 prototype : taskvtry_peek(ref singular message)
The peek() method copies one message from mailbox queue without removing message form mailbox. So it mailbox is empty than the current process blocks until the message gets into mailbox.

try_peek();

The try_peek() method attempts to copy message from mailbox without blocking.
Prototype :  function int try_peek(ref singular message);
Try peek method attempts to copy one message from the mailbox without removing the message from the mailbox queue. If mailbox is empty than it returns 0. 

Parametrize a Maibox

By default mailbox can be of any type. You can use mailbox to store int, string, user defined class etc. This means single mailbox can be used to send and receive any kind of data. This is really powerful mechanism but that also can lead us to run time error due to type mismatch between message in mailbox and type used to retrieve data as argument. For most of the cases mailbox is used to transfer some perticular type of message. In this case it is useful to detect type mismatch error during compile time. 

Prototype for parametrized mailbox is as follows :

     mailbox #(type = dynamic_type)

For example  we want to take mailbox of class ahb_data, this can be declare as follows.

    mailbox  #(ahb_data) mbx;

mbx is created mailbox of type ahb_data. So we can only add object of ahb_data only in this mbx mailbox non other than this type of data gives error.

The methods like get(), put(), try_get(), try_put(), num(), peek(), try_peek() same for normal mailbox as well as parametrized mailbox.



Semaphore in System Verilog

For verification purpose we need high level and easy to use synchronization and communication mechanism for our test bench.

Sytem verilog supports mailbox for communication between dynamic processes and semaphore for synchronization between dynamic processes.

Semaphore

Semaphore is like bucket of keys for processes. When a semaphore is created a bucket is created that has capability to contain fixed number of keys. The processes that uses semaphore must first get its keys and than only it is able to start its process. It is like token passing mechanism and as resources are always limited here number of token are also limited.

We will consider following example to understand Semaphore. 

Suppose we have two running processes and both of them access same interface. Both porcesses are independent of each other.  So it is possible that both the process starts driving the interface at the same time. If both processes are driving interface at the same time we will get wrong data on interface until and unless both processes are driving same data on same time !! But that is not going to happen anyway. This type of situations can be managed by keeping some flag that indicates that interface is free or not and our process starts if and only if interface is free. But what will happen if you have say m number of processes that is driving n (n < m) number of interfaces. For this kind of senariaos it will be dificult to syncronize between processes.
For such application semaphore is the best options. 

Lest assume that in some BFM two process write_data () & read_data() this both tasks are getting called randomly and they are using same interface to complete read or write operation. For such type of situation we can implement our read and write progreamn following way.
class semaphore_example;

  semaphore smphr = new(1);

    task read();
       smphr.get(1);       

       // Code to read data from interface

       smphr.put(1);
    endtask : read


task write();
       smphr.get(1);
    
       // Code to write data from interface

       smphr.put(1);
 endtask : write

      
endclass : semaphore_example

In the above example whenever the read or write task is getting called it first gets key form semaphore using smphr.get(1) task and than starts driving the interface and once done with the interface puts the key to the semaphore. This way whenever any task/process gets key from semaphore it is certain that interface is not driven by other process.

There are four methods are defined for semaphores which is listed down here. For more details you can use LRM for System Verilog.

New() ;

Prototype 
                    function new (int KeyCount = 0);
Then KeyCount indicates number of key that is initially allocated to the semaphore.

put ()

The semaphore method used put keys back into semaphore.
Prototype :   function void put(int KeyCount = 0);

get()

The method get() is used to procure specific number of key from semaphore.
Prototype : task get(int KeyCount = 0);
This task blocks the execution of the process until it gets the semaphore key.

Try_get();

The method is used to procure specific number of keys form semaphore but without blocking the process.
Prototype : function void try_get(int KeyCount = 0);











EXCLUSIVE access in AXI and use of it

EXCLUSIVE access provides semaphore type of mechanism without secrificing AXI bus performance and with little bit complexity in AXI Slave.
Here is some description about how exclusive access works. EXCLUSIVE access is performed in following steps. 
  • First master initiates exclusive read transaction with ARID to slave.
  • Once slave gets some exclusive read request for perticular address range it starts monitoring that perticular address range for change of value in that memory address. 
  • After some time master initiate exclusive write transaction to that slave with same transaction type and same AWID that was used in previous exclusive read transaction. 
  • Exclusive write transaction is responced :
    • Successful:  If data values at address which was monitored is not changed by other master 
    • Fail : If data values at which address which was being monitored has been changed or address to be monitored has been updated with same ARID as initiated by master in first step of the exclusive access transaction. 
     

We all might have some question that what is the advantage of using this exclusive access transaction for AXI ? 

Now consider a system in which two AXI master devices are using same memory or we can say in technical term using shared memory.  And as a system designer you always will make sure that at a time your one master does not overwrite your memory written by another master.
Consider AXI Master 1 (M1) has initiated exclusive read transaction for address location 12'h100 to 12'h10F. Now slave will start monitoring these addresses for ARID given by M1. Now till exclusive write operation is performed slave monitors that address and if that address is changed by another master M2  it will give indication of exclusive access failure during the exclusive write transaction and memory is dose not get updated by M1. 
What happened in above scenario is that Slave has reserved some memory resource for M1 virtually by given exclusive read request from master. When master comes for write transaction for that memory location slave will allow to write that memory resource if and only if another master device is not using that memory resource other wise data is not written to memory resource. 
This way we can avoid memory overwrite problem for shared memory using EXCLUSIVE Access in AXI.


Hope you got my view point in advantage of using AXI exclusive access.  Correct me if I am wrong at some place in my answer.

Thursday, 11 September 2014

AHB and AXI protocol response types


1. What are different response type for AHB and AXI and give use of the same?


Both supports 2 bit response signals.

AXI :

Total four response type is there  :
1) OKAY : 
Normal response used for indication that Normal access is succesfully completed and also used to indicate exclusive access has failed.

2) EXOKAY : 
Indicates exclusive access is successful. Or indiicates that either read or write portion of exclusive access has been successful. 

3)SLVERR : 
Indicates that request has been successfully reached to slave but slave has given some error to the request. 

4) DECERR : 
Indicates that interconnect is not able to find any slave that supports requested transaction address.

From above errors only three error are given by SLAVE AXI. While DECERR response should be given by Interconnect if it is not able to find suitable SLAVE for requested address from master device. Spec recommends to keep one default slave to which arbiter route request if it is not able to decode it and that default slave give DECERR response anytime it is requested. 

 AHB 

Four responses are there same as AXI  with different in indication. 

OKAY :
Indicates transfer is going normally and there is not error as long as this response comes from salve with HREADY signal high. This response indicates transfer has completed successfully.

ERROR :
This response indicates that last transfer was unsuccessful due to some ERROR. Master do not required to repeat the transaction if ERROR response arrived from slave/Arbiter.

RETRY : Indicates transfer can not completed immediately and master should request again the same data to slave. 

SPLIT : Indicates transfer is not completed immediately and master should request same data to slave when slave is ready for data transaction.


Difference between SPLIT and RETRY: 
--> Retry : Slave asks master to request again after sometime arbiter will not remove master from arbitration logic. 
--> SPLIT : Slave tell master that I will tell you when I am ready to serve you request till now arbiter will keep you out of arbitration logic.
.

Saturday, 6 September 2014

Reason not to use disable LABLE in System Verilog



Have you ever used disable LABLE to kill some process or task ? If yes than be careful while killing that particular process. As LABLE is static for class it will kill all the processes that has been created by that particular label. 

Suppose you have one class named main_class which contains task named main_run. It is quite possible that you have taken multiple instances of that class in your Verification Environment. Suppose you have taken three objects of that class and each has one task running main_run. You have written your code such a way that you need to kill that task when you get some particular condition or event or trigger. And you are using disable LABLE for that case. Now guess what happens when you call disable LABLE form any one object. All the main_run task will get killed while using that disable. 

So it is better to not use disable LABLE when you are taking multiple instance of your class. Or I would say if it is avoidable to use disable LABLE then do not use it.  



Disable LABE example

 

 // Code to describe brutal nature of disable LABLE method to kill a process

class main_class;
  bit a = 0;
  task main_run(string name = "not assigned");
    $display($realtime,$psprintf("Task from %s class called",name));
    fork
      begin
        wait(a == 1);
        $display($realtime,$psprintf("class %s Disabling TEST_LABLE",name));
        disable TEST_LABLE;
      end
    join_none

    begin  : TEST_LABLE
      #1000;
      $display($realtime,$psprintf("class %s After 1000 wait in main_class",name));
    end
  
    $display($realtime,$psprintf("Class %s end of the main_run task",name));
  endtask : main_run
endclass : main_class


module disable_lable();

    main_class m1;
    main_class m2;
    main_class m3;

 initial begin

    m1 = new ();
    m2 = new ();
    m3 = new ();

    fork
      m1.main_run("m1");    
      m2.main_run("m2");    
      m3.main_run("m3");    
    join_none

    #20;
    $display($realtime,"Before setting class disable");
    m1.a = 1;
 end

initial begin
 #100000;
end

endmodule : disable_lable


Output : 

# 0Task from m1 class called
# 0Task from m2 class called
# 0Task from m3 class called
# 20Before setting class disable
# 20class m1 Disabling TEST_LABLE
# 20Class m1 end of the main_run task
# 20Class m2 end of the main_run task
# 20Class m3 end of the main_run task



You can see from this example that just calling disable TEST_LABLE of object m1 is sufficient to kill all processes running under TEST_LABLE


Friday, 5 September 2014

Fine Grain Process control in System Verilog Test Bench

It most important to kill some particular processes when we have System reset or at such type of event when test bench required to kill some threads and restart the same. It is easy to start any thread as you just need to call it. Main effort of BFM developer is to kill running thread especially when task written is blocking. Blocking means it is taking too many clock cycles before checking arrival of Reset signal.

There are many ways to kill running processes in System Verilog. Some of them are disable fork, disable LABLE or you can create some flags that will break all consequent process and achieve your goal. 

disable fork is able to kill the process only if the condition to kill the process and fork-join_x block is in same scope.

disable LABLE is brutal way to kill your process as LABLE is static for class and it you kill one process using LABLE than all processes created under that particular gets killed. It will kill your other processes that is running in other objects of the class. You can see the example 

What if you can specify to kill process whenever you want just like push some button and there is a blast that kill tasks that is running right now !!

System Verilog provides such facility. It has inbuilt class named process. This class can not be extended.

Process Class

A process is a built in class in System Verilog. This class allows one process to control another process once it has started.  You can declare object of the process class, pass it to other class or task or take instance in another objects or in other words you can use instance of process class same as object of normal class. But there are some restriction while using process class which are as follows.
  • You can not extend process class.
  • You can not call new function of process class to create memory.
Above both attempts results in compilation error in System Verilog.
Objects of class process internally created by simulation when processed spawned. 
Process class contains following members.

typedef enum {FINISHED , RUNNING, WAITTING, SUSPENDED, KILLED} state;

This enum is mainly used to indicate current state of process.

static function  process self ()  
Function returns handle to the current process , that is, a handle to the process making the call.

function state status()
This function return current status of the process.
   If it returns FINISHDE than it indicates that process has terminated normally.
   If it returns RUNNIG than it indicates that process is running durring current time stamp.
   If it returns WAITTING than it indicates that waiting in blocking statement.
   If it returns SUSPENDEN than it indicates that process has been stopped and waiting for getting resume.
   If it returns KILLED than it indicates that process has been successfully killed via kill or disable.

function void kill();
This function terminated the given process and all its subsequent processes that is processes spawned using fork statements by the process being killed.

task await();
This task waits for the process to be finished successfully. This can be called from another process which is waiting for some process to be finished.

function void suspend();
This function suspends either its own execution or execution or that of another process.

function void resume();
This function resumes previously suspended process.

The methods kill(), await(), suspend() and resume() shall be restricted to a process created by initial, always or fork blocks only.

To understand how to use the process class to kill any task please consider the following example. you can understand from this example use of the process class and apply your understanding to your specific requirement.

Example

// Example to kill the processes in system Verilog.

class main_class;
  bit a = 0;
  task main_run(string name = "not assigned");
     $display($realtime,$psprintf("Task from %s class called",name));

     #1000;
  
     $display($realtime,$psprintf("Class %s end of the main_run task",name));
  endtask : main_run
endclass : main_class

module disable_lable();

    main_class m1;
    main_class m2;
    main_class m3;
    process p1;
    process p2;
    process p3;

  initial begin

    m1 = new ();
    m2 = new ();
    m3 = new ();

     fork
       begin       
         p1 = process :: self();
         m1.main_run("m1");   
         $display($realtime,"m1 main_run finished");
       end
       begin
         p2 = process :: self();
         m2.main_run("m2");   
         $display($realtime,"m2 main_run finished");
       end
       begin
         p3 = process :: self();
         m3.main_run("m3");   
         $display($realtime,"m3 main_run finished");
       end
     join_none

     #20;
     $display($realtime,"Before killing p1 process");
     p1.kill;
       
  end
initial begin
  #100000;
end

endmodule : disable_labl

Output

# 0Task from m1 class called
# 0Task from m2 class called
# 0Task from m3 class called
# 20Before killing p1 process
# 1000Class m2 end of the main_run task
# 1000m2 main_run finished
# 1000Class m3 end of the main_run task
# 1000m3 main_run finished


From above example you can see that main_run task is called in fork join_x from three different classes. Now for each begin end block contains one call of task and one process handle created. From the above example you can see that we can kill the task whenever we want to kill by just calling process obj.kill(); method of class process. Output of the above code is shown above.