Friday 29 May 2015

Streaming Operator in System Verilog

class transaction;

  bit [7:0] addr;
  bit [7:0] csm;
  bit [7:0] data;

  function void pack(ref bit [23:0] bytes[$]);
    bytes = { >> {addr, csm, data}};
  endfunction : pack
 
  function void unpack(ref bit [23:0] bytes[$], ref transaction tr);
    { >> {tr.addr, tr.csm, tr.data}} = bytes;
  endfunction : unpack

endclass : transaction

module top();
  transaction tr;
  transaction tr2;
  bit [23:0] bytes[$];

  initial begin
    tr = new();
    tr.addr = 8'hFF;
    tr.csm  = 8'hAA;
    tr.data = 8'h55;
    void '(tr.pack (bytes));
    foreach (bytes[i])
    begin
      $display("bytes[%0d]=%0h", i, bytes[i]);
    end

    tr2 = new();
    tr.unpack(bytes, tr2);
    $display("tr2 : addr=%h, csm=%h, data=%h", tr2.addr, tr2.csm, tr2.data);
  end
endmodule : top


//Output:
//  bytes[0]=ffaa55
//  tr2 : addr=ff, csm=aa, data=55

Shallow Copy vs Deep Copy in System Verilog

// SHALLOW COPY

// All the variables are copied however objects are not copied, only thier handles are copied.
// i.e. Shallow copy does not copy objects.
// Any nested objects will NOT be duplicated, only the members themselves

class baseA;
  int unsigned j = 5;
endclass : baseA

class B;
  int unsigned i = 1;
  baseA a = new;
endclass : B

class xtndA extends baseA;
  rand int unsigned x;
  constraint cn1 {
    x < 10;
  }
endclass : xtndA

module top();
  xtndA xtnd1;
  baseA base2;
  baseA base3;
  B     b1;
  B     b2;
  int unsigned test1;
  int unsigned test2;

  initial begin
    b1 = new();         // Creates an object of class B
    b2 = new b1;        // Creates a SHALLOW COPY of b1.

                              //Handle 'a' will be copied but not the object it points to.
    //b2 = new() b1;    // Note: () is not allowed after new

    b2.i   = 10;        // i is changed in b2, but not in b1
    b2.a.j = 50;        // Chaged a.j, shared by both b1 and b2

    test1 = b1.i;       // test1 is set to 1 (b1.i has not changed)
    test2 = b1.a.j;     // test2 is set to 50 (a.j has changed)

    xtnd1   = new();    // Creates an object of class xtndA
    xtnd1.x = 3;        // 3 is assigned to x of xtnd1

    base2 = xtnd1;      // base2 refers to the same object as xtnd1
    base3 = new base2;  // Creates a SHALLOW COPY of xtnd1 (base2 refers to xtnd1)
    $display("===========================");
    $display("b2.i=%0d,  b2.a.j=%0d", b2.i,  b2.a.j);
    $display("b1.i=%0d,  b1.a.j=%0d", b1.i,  b1.a.j);
    $display("test1=%0d, test2=%0d",  test1, test2);
    $display("===========================");
  end
endmodule : top


//Output:
// ===========================
// b2.i=10, b2.a.j=50
// b1.i=1,  b1.a.j=50
// test1=1, test2=50
// ===========================



// DEEP COPY (Full copy)

// Everything including nested objects are copied.
// Custom code is typically needed.

class baseA;
  int unsigned j = 5;
  function baseA copy();
    copy = new;
    copy.j = j;
  endfunction : copy

endclass : baseA

class B;
  int unsigned i = 1;
  baseA a = new;
  function B copy();
    copy = new;
    copy.i = i;
    copy.a = a.copy();    // Call copy() of baseA
  endfunction : copy

endclass : B

module top();
  B     b1;
  B     b2;


  initial begin
    b1 = new();         // Creates an object of class B
    b2 = b1.copy();     // Creates a DEEP COPY of b1. Object 'a' is also copied.

    b1.i   = 10;        // i is changed in b1, but not in b2.
    b1.a.j = 50;        // Chaged a.j of b1 only, it is not shared with b2.

    $display("===========================");
    $display("b2.i=%0d,  b2.a.j=%0d", b2.i,  b2.a.j);
    $display("b1.i=%0d,  b1.a.j=%0d", b1.i,  b1.a.j);
    $display("===========================");
  end
endmodule : top


//Output:
// ===========================
// b2.i=1,  b2.a.j=5
// b1.i=10,  b1.a.j=50
// ===========================




Tuesday 12 May 2015

Automatic Variable in fork...join_none


My intention was to start several parallel threads inside a for loop and pass loop variable as an input. I assumed the following code would do the job:

module top;
  initial begin
    for (int i = 0; i < 3; i++)
      fork
        some_task(i);
      join_none
  end
  task some_task(int i);
    $display("i = %d", i);
  endtask
endmodule


Do you see the problem with this code?
If not, don't worry as I didn't at first either. Here is the output if you run it:

//Output:
//i =           3
//i =           3
//i =           3


It seems that the for loop executed completely and only then did the spawned processes start, but they were given the latest value of i.

After digging around on the Internet I discovered the answer. The SystemVerilog LRM mentions that "spawned processes do not start executing until the parent thread executes a blocking statement". This explains why the for loop finished executing and why by the time the processes were spawned i had already reached the value '3'.

The LRM also shows exactly how to fix our problem: "Automatic variables declared in the scope of the fork...join block shall be initialized to the initialization value whenever execution enters their scope, and before any processes are spawned". Applying this to our code yields:

module top;
  initial begin
    for (int i = 0; i < 3; i++) begin

      automatic int j = i;
      fork

        begin
          some_task(j);

          //
        end
      join_none

    end
  end
  task some_task(int i);
    $display("i = %d", i);
  endtask

endmodule

Now, for every loop iteration a new variable is allocated, which is then passed to the respective task. Running this example does indeed give the desired result:

//Output:
//i =           2
//i =           1
//i =           0

SystemVerilog Enumerated Types : Same literal in more than one enumerated types

A well known SystemVerilog limitation is that the same literal cannot appear in more enumerated types within a package (or more precisely within a scope).
 
Let's look at a concrete example.

package my_pkg1;
  typedef enum {
    NONE,
    TX,
    RX

  } direction_e;

  typedef enum {
    NONE,
    ADD,
    SUB

  } math_action_e;
endpackage : my_pkg1

module top();
  import my_pkg1 :: *;
  direction_e dir_type;
  math_action_e math_action;

  initial begin
    dir_type    = my_pkg1 :: NONE;
    math_action = my_pkg1 :: NONE;
    $display("dir_type=%s, math_action=%s", dir_type.name(), math_action.name());
  end
endmodule : top

As discussed above, this code won't compile, because NONE is declared in both types.

//Output:
//** Error: Enum literal name 'NONE' already exists.


What I've seen people do in this case is try to uniquely the names by adding either prefixes or suffixes. For example, the math_action_e type would contain the value NONE2, in order not to clash with the NONE from direction_e.


Other simple solution would be to define each type in its own package. In the main package we would then import both of these packages:

package my_pkg1;
  typedef enum {
    NONE,
    TX,
    RX

  } direction_e;
endpackage : my_pkg1

package my_pkg2;
  typedef enum {
    NONE,
    ADD,
    SUB

  } math_action_e;
endpackage : my_pkg2


module top();
  import my_pkg1 :: *;
  import my_pkg2 :: *;

  direction_e dir_type;
  math_action_e math_action;

  initial begin
    dir_type        =  my_pkg1 :: NONE;
    math_action  =  my_pkg2 :: NONE;
    $display("dir_type=%s, math_action=%s", dir_type.name(), math_action.name());
  end
endmodule : top
We've solved the collision problem, because each type is now defined in its own scope.


In above example if I assign NONE directly without using any scope then simulator would cowardly refuse to compile the code above, because the literal NONE was imported via wildcards multiple times and it's ambiguous.

  initial begin
    dir_type        =  NONE;
    math_action  =  NONE;
    $display("dir_type=%s, math_action=%s", dir_type.name(), math_action.name());
  end

//Output:
//** Error: Identifier 'NONE' is not directly visible.
//   Found multiple Declaration of 'NONE' through wildcard imports from these packages : my_pkg1, my_pkg2
//** Error: Undefined variable: 'NONE'.
You can read more about it here

Friday 8 May 2015

Down Cast in SystemVerilog and virtual concept in SystemVerilog

// Command to run in questa
// 1. vlib work
// 2. vlog -sv top.sv
// 3. vsim top -c
// 4. run -all (in vsim prompt)

class base;

  int a = 5;
endclass

class extend extends base;
  int b = 1;
endclass

module top;
  initial begin
    base     m_base;
    extend  m_extend;
    
    m_extend = new();
    m_base    = new();
    $cast(m_extend, m_base);
    $display(m_extend.a);
  end
endmodule

//Error: (vsim-3971) $cast to type 'class work.top1_sv_unit::extend' from 'class work.top1_sv_unit::base' failed in file top.sv

Meaning of a successfully casting from super-class(parent class) to sub-class(child class) is that the super-class is actually a sub-class.

class BasePacket;
  int A = 1;
  int C = 2;

  function void printA;
    $display("BasePacket::A is %d", A);
  endfunction : printA

  virtual function void printC;
    $display("BasePacket::C is %d", C);
  endfunction : printC
endclass : BasePacket

class My_Packet extends BasePacket;
  int A = 3;
  int C = 4;

  function void printA;
    $display("My_Packet::A is %d", A);
  endfunction: printA

  virtual function void printC;
    $display("My_Packet::C is %d", C);
  endfunction : printC
endclass : My_Packet

module top;
  initial begin
My_Packet EXT = new;
BasePacket BASE;

BASE = EXT;
BASE.printA;
BASE.printC;
$cast(EXT, BASE);
EXT.printA;
EXT.printC;
end
endmodule : top

//Output:
// BasePacket::A is           1
// My_Packet::C is           4
// My_Packet::A is           3
// My_Packet::C is           4

Here super-class in this example (BASE) is actually the sub-class (EXT), so the $cast is passed.

Here for first call (using BASE handle) of printA and printC, super-class (BASE) is acutally sub-class (EXT) and function printC is declared as virtual in super-class (BASE), so it will execute printC function of sub-class (EXT).
But, function printA is not declared as virtual in super-class (BASE), so it will execute printA function of super-class (BASE), in-spite of it is actually having memory of EXT class, because handle is of super-class.

One usual scenario to use the $cast is to pass some sub-class data into other class, and the developer of the receptor class even don't know if the incoming class is what kind of sub-class, so usually he will in the receptor class use a super(parent)-class to get the incoming data and then use $cast to check/transfer the data into certain sub-class.