cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Highlighted
Voyager
Voyager
888 Views
Registered: ‎04-12-2012

Systemverilog One Hot to Binary function

Jump to solution

Hello,

My design requires converting a One Hot encoded vector to Binary from.
For this purpose - I want to write a generic function that can accept any size of vector.
My problem is that Systemverilog doesn't allow the function to have an unconstrained array input.

How can I solve this ?
This is what I wrote so far:

function one_hot_to_binary ( logic /*missing_type_and_size*/ vector_one_hot ) ;
logic [ $clog2 ( $size ( vector_one_hot ) ) - 1 : 0 ] vector_binary = 0 ; // Define an appropriately sized binary vector.
    foreach ( vector_one_hot [ index ] )
    begin
        if ( vector_one_hot [ index ] == 1'b1 ) // If the bit index is '1' do a logic OR with the index value.
        begin
            vector_binary = vector_binary | index ;
        end
    end    
    return vector_binary ;
endfunction
0 Kudos
1 Solution

Accepted Solutions
Highlighted
Scholar
Scholar
458 Views
Registered: ‎09-16-2009

Ok, here's how to do it within an interface.  Note that at the times I really need such a parameterized function, I usually already have an interface associated with it.  So I'd use the existing interface.  The example below creates the interface just to define the function - so there's more overhead.

interface some_if
#(
  parameter ARRAY_SIZE = 8;
)();
  localparam ARRAY_SIZE_BITS = $clogb2( ARRAY_SIZE) ;

  function bit [ ARRAY_SIZE_BITS - 1 : 0 ] one_hot_to_binary ( input bit [ ARRAY_SIZE - 1 : 0 ] vector_one_hot );
  //same function definition as before
  endfunction
endinterface

module top;
  localparam ARRAY0_SIZE = 8;
  bit [ ARRAY0_SIZE - 1 : 0 ] one_hot_vector0;
  wire [ $clogb2( ARRAY0_SIZE ) - 1 : 0 ] one_hot_binary0;

  //instantiate the interface of the appropriate size
  some_if #( .ARRAY_SIZE( ARRAY0_SIZE ) ) array0_if();
 
  assign one_hot_binary0 = array0_if.one_hot_to_binary( one_hot_vector0 );
endmodule

The some_if() interface (of whatever size) could then be instantiated in any module that needed the parameterized function.

Regards,

Mark

View solution in original post

14 Replies
Highlighted
Scholar
Scholar
878 Views
Registered: ‎09-16-2009

Unconstrained arrays are not synthesizable in Vivado, unfortunately.  

See this thread for some alternatives.  None of the alternatives are ideal, but it is what it is.

https://forums.xilinx.com/t5/Synthesis/Vivado-2016-1-regression-can-not-call-functions-with-parameters/td-p/694102 

Regards,

Mark

0 Kudos
Highlighted
Voyager
Voyager
857 Views
Registered: ‎04-12-2012

OK,

I'll read about it.

BTW, I constrained the arrays just to test it and it still doesn't work.

This is the code:

function one_hot_to_binary ( logic [ 7 : 0 ] vector_one_hot ) ;
automatic logic [ 2 : 0 ] vector_binary = 0 ;
foreach ( vector_one_hot [ index ] )
begin
if ( vector_one_hot [ index ] == 1'b1 )
begin
vector_binary = vector_binary | index ;
end
end
return vector_binary ;
endfunction

logic [2:0] result_0 = one_hot_to_binary ( 8'b00000001 ) ; // result is 0 in simulation - expected 0
logic [2:0] result_1 = one_hot_to_binary ( 8'b00000010 ) ; // result is 1 in simulation - expected 1
logic [2:0] result_2 = one_hot_to_binary ( 8'b00000100 ) ; // result is 0 in simulation - expected 2
logic [2:0] result_3 = one_hot_to_binary ( 8'b00001000 ) ; // result is 1 in simulation - expected 3
logic [2:0] result_4 = one_hot_to_binary ( 8'b00010000 ) ; // result is 0 in simulation - expected 4
logic [2:0] result_5 = one_hot_to_binary ( 8'b00100000 ) ; // result is 1 in simulation - expected 5
logic [2:0] result_6 = one_hot_to_binary ( 8'b01000000 ) ; // result is 0 in simulation - expected 6
logic [2:0] result_7 = one_hot_to_binary ( 8'b10000000 ) ; // result is 1 in simulation - expected 7

Do you see something wrong with it ?

0 Kudos
Highlighted
Scholar
Scholar
837 Views
Registered: ‎09-16-2009

Start by fixing the return type of the function:

function bit [ 2 : 0 ] one_hot_to_binary ( logic [ 7 : 0 ] vector_one_hot );

I always thought the implied return type of a function was an integer (at least in Verilog-1995).  But it seems the implied return type for SystemVerilog is a single-bit logic scalar.  I don't really know as I always explicitly indicate the function return type.

Regards,

Mark

Highlighted
Voyager
Voyager
753 Views
Registered: ‎04-12-2012

Thanks Mark.
This indeed solved the problem.
So the lack of support for unconstrained arrays is a VIvado limitation ? The language supports it ?

0 Kudos
Highlighted
Scholar
Scholar
735 Views
Registered: ‎09-16-2009

SystemVerilog has a few types of dynamic arrays (dynamic arrays, associative arrays, and queues).   Classes are dynamic objects as well.  However none are supported by any synthesis tool that I'm aware of.  Simulation tools, of course, but not synthesis.

Your use case of using dynamic arrays to create a more flexible function definition would be a useful one.  However I think that's more the exception than the rule.  For a vendor to start supporting these type of dynamic types (within synthesis) where in the end, everything must be statically bound... Well I think this would creates more problems than benefits. Verilog from the beginning was a "static sized" language.  Everything was statically sized by the time elaboration was done.  So the tools written for the last 25 years or so pretty much had this within the foundation of the tool.  Sure VHDL had a more dynamic components the whole time.  But even there, support was iffy at best (one couldn't have VHDL unconstrained arrays in portlists for example).

Regards,

Mark

Highlighted
Voyager
Voyager
716 Views
Registered: ‎04-12-2012

@markcurry wrote:

Sure VHDL had a more dynamic components the whole time.  But even there, support was iffy at best (one couldn't have VHDL unconstrained arrays in portlists for example).


I don't know about the past - but it does now. You can write your VHDL entity with unconstrained ports and have it bound by connecting a constrained signal to it during instantiation.

Can you post an example of how you'd work around this in Systemverilog by the technique you suggest in the referenced post?

I tried to do it myself and wasn't able to make it work.

0 Kudos
Highlighted
Scholar
Scholar
695 Views
Registered: ‎08-01-2012

@markcurry 

Unconstrained ports have been allowed since VHDL 1993 (and have worked fine in synthesis for at least 15 years). Size is determined by the connection:

 

entity some_ent is
  port (
    some_dynamic_port : in std_logic_vector;  -- note no size
  ....
  );

....
signal a : std_logic_vector(7 downto  0);

inst : lib.some_ent
port map (
  some_dynamic_port => a

size information inside some_ent can be found via attributes 'length, 'range etc.

0 Kudos
Highlighted
Voyager
Voyager
686 Views
Registered: ‎04-12-2012

My request was for an example that mimics this behavior in Systhemverilog using a workaround that'd be synthesizable.

0 Kudos
Highlighted
Scholar
Scholar
651 Views
Registered: ‎09-16-2009

@richardhead 

I'd not know that synthesis supported this in VHDL.  As it is, I only use VHDL for vendor provided IP - the bulk of all my designs are written in verilog.  And since verilog couldn't accept such a construct across the mixed-language boundary, none of our provided IP could use it, and thus I wasn't aware of it.

To be clear, this only kicks the problem down the road (well, up the hierarchy).  At some point up the hierarchy, the "unconstrained" array must become fixed.   For verilog users, we've just lived with passing down that additional parameter (indicating the array/port size), since the language until recently, had no other way of expressing such.

@shaikon , if I have time I'll post an example of creating a parameterized function using an interface.  To be clear however, I don't think you want that for this use case. Passing down the array length (as another parameter) is the easiest solution.  There are other more advanced methodologies using SystemVerilog, that one can use to make shared IP more "object oriented" if you will, and auto derive widths, and other dimensions from the object being worked on.  These are advanced use cases, and not used by many as yet.  

Regards,

Mark

Highlighted
Voyager
Voyager
496 Views
Registered: ‎04-12-2012

Passing down the array length (as another parameter) is the easiest solution

Can you show an example of this ?

0 Kudos
Highlighted
Scholar
Scholar
486 Views
Registered: ‎09-16-2009

Here's an example - not checked for syntax errors.

 

module sub_module
#(
  parameter ARRAY_SIZE = 8;
)
(
  input  wire [ ARRAY_SIZE - 1 : 0 ] vector_one_hot_i,
  output wire [ $clogb2( ARRAY_SIZE) - 1 : 0 ] one_hot_binary_o
);
  localparam ARRAY_SIZE_BITS = $clogb2( ARRAY_SIZE) ;

  function bit [ ARRAY_SIZE_BITS - 1 : 0 ] one_hot_to_binary ( input bit [ ARRAY_SIZE - 1 : 0 ] vector_one_hot );
    bit [ ARRAY_SIZE_BITS - 1 : 0 ] vector_binary;
vector_binary = 0; foreach ( vector_one_hot [ index ] ) begin if ( vector_one_hot [ index ] == 1'b1 ) vector_binary = vector_binary | index ; end return( vector_binary ); endfunction assign one_hot_binary_o = one_hot_to_binary( vector_one_hot_i ); endmodule module top; localparam ARRAY0_SIZE = 8; bit [ ARRAY0_SIZE - 1 : 0 ] one_hot_vector0; wire [ $clogb2( ARRAY0_SIZE ) - 1 : 0 ] one_hot_binary0; sub_module #( .ARRAY_SIZE( ARRAY0_SIZE ) ) sub_module0( .vector_one_hot_i( one_hot_vector0 ), .one_hot_binary_o( one_hot_binary0 ) ); localparam ARRAY1_SIZE = 14; bit [ ARRAY1_SIZE - 1 : 0 ] one_hot_vector1; wire [ $clogb2( ARRAY1_SIZE ) - 1 : 0 ] one_hot_binary1; sub_module #( .ARRAY_SIZE( ARRAY1_SIZE ) ) sub_module1( .vector_one_hot_i( one_hot_vector1 ), .one_hot_binary_o( one_hot_binary1 ) ); endmodule

Here, the function is parameterized (for array size) by nature of its definition being created within a parameterized module.  We pass down the desired ARRAY_SIZE from the top module.

The tricky bit, that the other thread mentions, is how to define this function in some other "shared" file that can be included in many places.  This is where independently parameterized functions would help ( which SystemVerilog does not have as a feature).  The natural place one would want to place this function is within a SystemVerilog package, where it could be imported as desired.  However packages cannot be parameterized.  The other thread I mentioned shows ways to do this with SystemVerilog interfaces (which can be parameterized).  SystemVerilog classes can be parameterized too - one could bury the parameterized function within a parameterized class. However classes are not synthesizable, so using that solution has use cases limited to simulation only.

Regards,

Mark

 

Highlighted
Voyager
Voyager
473 Views
Registered: ‎04-12-2012

Thanks for the elaborate explanation.

I was afraid that this is what you're going to propose. Because it immediately hits the wall of:

The tricky bit, that the other thread mentions, is how to define this function in some other "shared" file that can be included in many places.

If you ever have the time to show show an example of how it can be done with interfaces it'll be very helpful.

 

0 Kudos
Highlighted
Scholar
Scholar
459 Views
Registered: ‎09-16-2009

Ok, here's how to do it within an interface.  Note that at the times I really need such a parameterized function, I usually already have an interface associated with it.  So I'd use the existing interface.  The example below creates the interface just to define the function - so there's more overhead.

interface some_if
#(
  parameter ARRAY_SIZE = 8;
)();
  localparam ARRAY_SIZE_BITS = $clogb2( ARRAY_SIZE) ;

  function bit [ ARRAY_SIZE_BITS - 1 : 0 ] one_hot_to_binary ( input bit [ ARRAY_SIZE - 1 : 0 ] vector_one_hot );
  //same function definition as before
  endfunction
endinterface

module top;
  localparam ARRAY0_SIZE = 8;
  bit [ ARRAY0_SIZE - 1 : 0 ] one_hot_vector0;
  wire [ $clogb2( ARRAY0_SIZE ) - 1 : 0 ] one_hot_binary0;

  //instantiate the interface of the appropriate size
  some_if #( .ARRAY_SIZE( ARRAY0_SIZE ) ) array0_if();
 
  assign one_hot_binary0 = array0_if.one_hot_to_binary( one_hot_vector0 );
endmodule

The some_if() interface (of whatever size) could then be instantiated in any module that needed the parameterized function.

Regards,

Mark

View solution in original post

Highlighted
Scholar
Scholar
450 Views
Registered: ‎09-16-2009

Just for completeness sake for anyone reading along, here's how to do it with a SystemVerilog class - again a class is not synthesizable.

 

package some_utility_pkg;
class some_class #( parameter ARRAY_SIZE = 8; ); localparam ARRAY_SIZE_BITS = $clogb2( ARRAY_SIZE) ; static function bit [ ARRAY_SIZE_BITS - 1 : 0 ] one_hot_to_binary ( input bit [ ARRAY_SIZE - 1 : 0 ] vector_one_hot ); //same function definition as before endfunction endclass
endpackage

module top;
localparam ARRAY0_SIZE = 8;
bit [ ARRAY0_SIZE - 1 : 0 ] one_hot_vector0;
wire [ $clogb2( ARRAY0_SIZE ) - 1 : 0 ] one_hot_binary0;

assign one_hot_binary0 = some_utility_pkg::some_class#( ARRAY0_SIZE )::one_hot_to_binary( one_hot_vector0 );
endmodule

Note that the class is never constructed - since the function is static, the use of the class scope resolution is allowed.  A little bit of mouthful, but the use is clear.  I use these types of things in my behavioral modelling code in SystemVerilog quite often.

Regards,

Mark