Jump to content
farhad01

How to pass variable sized packed arguments to a task/function?

Recommended Posts

In SystemVerilog we can have dynamic unpacked arrays and they can be passed to a function/task. I was wondering if there is a way to pass dynamic packed arrays to a function/task. For example consider the following code:

 

module test;
  logic [3:0] A;
  logic [7:0] B;

  task automatic double(ref [3:0] val);
    val = val * 2;
    $display("%b",val);
  endtask

  initial begin
    A = 3;
    double(A);
    B = 5; 
    //double(; ** Error because of size mismatch
  end

endmodule

here the task can only have a 4-bit input argument so if B is passed an error occurs. I am interested to know if there is any way to pass packed arrays of different size to a task/function. In previous example if the arrays were unpacked I could use:

 

  task automatic double(ref val []);

 

but I have no idea what I should use for packed arrays. In VHDL having variable size input arguments is very easy. For example the same code can be written like this:

 

use std.textio.all;

entity test is
end entity;

architecture arch of test is

  procedure double(val: bit_vector) is
    variable temp : bit_vector(val'left downto val'right);
    variable l : line;
  begin
    temp := val sll 1;
    write(l,temp);
    writeline(output,l);
  end procedure;

begin

  process
    variable A : bit_vector(3 downto 0);
    variable B : bit_vector(7 downto 0);
  begin
    A := "0011";
    double(A);
    B := "00001111";
    double(;
    wait;
  end process;
  
end architecture;

I appreciate any idea on this.

 

Thanks

 

 

 

 

Share this post


Link to post
Share on other sites

I was wondering if there is a way to pass dynamic packed arrays to a function/task.

 

Well, there are no dynamic packed arrays in System Verilog.

 

If the logic of your function is independent of the width of the data, as in your example, then you can use a parameterized class that supply you with the required function.

 

For example:

 

 


class doubler #(int unsigned WIDTH = 1);

  static task double(ref [WIDTH-1:0] val);
    val = val * 2;
    $display("%b",val);
  endtask

endclass

module test;
  logic [3:0] A;
  logic [7:0] B;

  initial begin
    A = 3;
    doubler #(4)::double(A);
    B = 5; 
    doubler #(8)::double(;
  end

endmodule

Share this post


Link to post
Share on other sites

Thanks Zvi for your solution, but your solution requires passing the size of argument with it.

It is too bad that SV is a rich language but it lacks such a trivial feature as already found in VHDL.

Share this post


Link to post
Share on other sites

If you require to be unaware of the width of your vector, you can use this:

doubler #($bits(A))::double(A);

 

Yes, its quite a verbose (and ugly) syntax, but on the other hand, VHDL is quite verbose (and ugly  :)) as well...

 

Regarding SV lacking this feature - well, it does make sense to me, at least when considering simulators performance (static vs. dynamic arrays, optimization, etc...)

On the other hand, the language does enable you to use dynamic arrays. Moreover, you may quite easily convert them to packed vectors with the streaming operator.

Share this post


Link to post
Share on other sites

If you want to add "missing" features into a SV testbench, it can be very easy using DPI, provided you know basic C/C++ syntax.

 

For example, here is a version of your double task that takes an open array (assumed here to be a packed bit vector of any length). I haven't included any checking/error handling but even so, this should still be OK for up to 32-bit vectors.

#include "svdpi.h"

extern "C" {

int double_vec (svOpenArrayHandle h) {
  svBitVecVal *ptr;
  int size;
  int value;
  size = svSize(h,0);

  //get bit vector ptr from handle
  ptr = (svBitVecVal*)svGetArrayPtr(h);
  //convert to int
  value = bv_to_int(ptr,size);
  value *= 2;
  //write int back to bv
  int_to_bv(value,ptr,size);
  return 0;
}
} 

 

The functions to convert to/from int/bit vector could be something like:

 

int bv_to_int(const svBitVecVal* bv, int size) {
  int val = 0;
  int mask = 1;
  for (int i=0; i<size; i++){
      if (svGetBitselBit(bv,i) == sv_1)
          val |= mask;
      mask <<= 1;
  }
  return val;
}

void int_to_bv (int val, svBitVecVal* bv, int size) {
  int mask = 1;
  svBit b;
  for (int i=0; i<size; i++){
      b = (val & mask) ? sv_1 : sv_0;
      svPutBitselBit(bv,i,;
      mask <<= 1;
  }
}

 

You could declare and use the DPI function in SV as follows:

 

module top();
  import "DPI-C" task double_vec(inout bit[] vec);
 
  bit [7:0] vec8 = 8'd15;
  bit [15:0] vec16 = 16'd1234;
 
  initial begin
    $display ("vec8 = %0d, vec16 = %0d", vec8, vec16);  
    double_vec(vec8);
    double_vec(vec16);
    $display ("vec8 = %0d, vec16 = %0d", vec8, vec16);  
  end

endmodule: top
 

With simple uses of DPI such as this, most current SV simulators will automatically compile and link the SV and C/C++ code for you, e.g.

qverilog top.sv double.cpp 

 

Hope that gives you some ideas, even if it is a somewhat more complicated solution than you were hoping for!

 

Regards,

Dave

 

 


 


 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×