Saturday, 13 September 2014

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.