VHDL: use the length of an integer generic to determine number of select lines
Asked Answered
F

5

14

I'm trying to create a reusable barrel shifter; it takes an input array of bits and shifts them a certain number of positions (determined by another input). I want to parameterize the module so that it works for any n.

The number of select lines required is determined by n --> i.e., SHIFT_CNT = log2(NUMBITS-1)+1 in the code below. It's considered bad form in my organization (and I think overall) to have ports that are not of std_logic_vector or std_logic, so I used a std_logic_vector for the number of select lines. I need to adjust the length of the std_logic_vector based on the input generic. Is there a way to do this without using a second generic? I've seen this post, but it doesn't deal with generics. This post eliminates the generics entirely or uses the log value as the generic, which isn't as intuitive to future users (and could cause problems if the INPUT is not a power of two).

The declaration of SHIFT_CNT below is definitely incorrect; is there a way to automatically generate the length in the entity declaration without using a second generic?

entity BarrelShifter is

generic ( NUMBITS : integer :=8);                                                   
Port    ( INPUT     : in   std_logic_vector (NUMBITS-1 downto 0);                
          OUTPUT    : out  std_logic_vector (NUMBITS-1 downto 0);                
          SHIFT_CNT : in   std_logic_vector ((NUMBITS-1)'length downto 0)                 
        );                                                               
end BarrelShifter;
Feu answered 5/10, 2012 at 16:4 Comment(0)
C
23

You can use the math library to calculate log2 and ceil of the logarit result to declare the size of SHIFT_CNT.

use IEEE.math_real.all;

or specific functions

use IEEE.math_real."ceil";
use IEEE.math_real."log2";

For example you want to calculate clog2 of value a

result := integer(ceil(log2(real(a))));

If you just use these function to calculate paramater, your code is synthesizable (I did it).

If you don't want use it in entities, you can declare them in a library or generic with these functions.

Climate answered 6/10, 2012 at 7:18 Comment(6)
Thanks Khanh; I did this as a stopgap, and the ceil and log2 functions somehow always give me an extra bit. It gets pruned off anyway, but I was hoping there was something more compact that didn't require libraries or additional FPGA resources.Feu
It mean you want floor of log2 for smaller bit. For example, result of log 2 is 1.34, the ceil is 2 and floor is 1. I think this required library is standard (IEEE standard) and don't care about it. Another method is use 2 parameter to declare. At top file, you can use this function.Climate
@user1723509, using a library in a way described in this or the other answer shouldn't use any additional FPGA resources at all, since the function's output can be resolved during compile/synthesis time.Ground
Due to rounding issues, this may yield unexpected results, especially when used with floor...Tartary
result := integer(ceil(log2(real(a) + real(1)))); to prevent rounding errors, e.g. when a = 8.Allenaallenby
The examples using function names as operator symbols in string literals are specifically semantic errors, e.g. IEEE Std 1076-2008 4.2.1 "designator ::= identifier | operator_symbol operator_symbol ::= string_literal", 4.5.2 Operator overloading "The sequence of characters of the operator symbol shall be one of the operators in the operator classes defined in 9.2." Neither ceil nor long are operators.Flickinger
E
9

You can create a log2-function in a library, like this:

   function f_log2 (x : positive) return natural is
      variable i : natural;
   begin
      i := 0;  
      while (2**i < x) and i < 31 loop
         i := i + 1;
      end loop;
      return i;
   end function;

If the library is imported you may then specify the port like this:

shift_cnt : in std_logic_vector(f_log2(NUMBITS)-1 downto 0)

It is a somewhat ugly solution, but it doesn't use any resources (since the function is pure and all the inputs are known at compile time).

I usually do this, but you may prefer specifying the log value as the generic like you're mentioning.

Elf answered 5/10, 2012 at 17:38 Comment(2)
That was one of my concerns -- I didn't want to write a function that would require FPGA resources to calculate. I'd still like something that is totally self-contained if possible (as opposed to writing a library and then remembering to include it anywhere I need it)Feu
I can see why you would want that (I would too, since it's such a common problem!) but I'm pretty sure there's not a more elegant way to do it. In my company we have a library with common functions such as this one that we import in all source files.Elf
Y
5

Two alternative approaches:

You could work it backwards and have the generic as shift_bits - then calculate the width of the input and output vectors from that:

generic ( shift_bits: integer :=3);                                                   
Port    ( INPUT     : in   std_logic_vector ((2**shift_bits)-1 downto 0);                
          OUTPUT    : out  std_logic_vector ((2**shift_bits)-1 downto 0);                
          SHIFT_CNT : in   std_logic_vector (shift_bits-1 downto 0)                 
        ); 

Or treat the count as a number:

generic ( NUMBITS : integer :=8);                                                   
Port    ( INPUT     : in   std_logic_vector (NUMBITS-1 downto 0);                
          OUTPUT    : out  std_logic_vector (NUMBITS-1 downto 0);                
          SHIFT_CNT : in   integer range 0 to numbits-1                 
        );  

and let the tools figure it out for you.

Yoder answered 8/10, 2012 at 12:51 Comment(1)
I think in case BITWIDTH is not 2 power, the first declaration is not suitable, second way is better. I did some module with integer/nature type in/out but my colleague did not accept. So any problem with this? Is fix-sized obtained after synthesis?Climate
T
1

When I was using the method mentioned by Khan, I encountered rounding errors. So I wrote my own versions, that are immune to rounding errors and can, in principle handle more than 32 bit. You can substitute the type of L with any Type that has a logical shift left operator.

Most of the time you want to use log2ceil which is the amount of bits required to store the given number, while log2floor can be more described as the highest bit set.

In most cases those functions are fine for synthesis as they are used for generating constants. So no hardware is inferred for them.

function log2ceil (L: POSITIVE) return NATURAL is
    variable i, bitCount : natural;
begin
    i := L-1;
    bitCount:=0;
    while (i > 0) loop
        bitCount := bitCount + 1;
        i:=srlInteger(i,1);
    end loop;
    return bitCount;
end log2ceil;

function log2floor (L: POSITIVE) return NATURAL is
    variable i, bitCount : natural;
begin
    i := L;
    bitCount:=0;
    while (i > 1) loop
        bitCount := bitCount + 1;
        i:=srlInteger(i,1);
    end loop;
    return bitCount;
end log2floor;

function srlInteger(arg: integer; s:natural) return integer is
begin
    return to_integer(SHIFT_RIGHT(to_UNSIGNED(ARG,32), s));
end srlInteger;
Tartary answered 19/5, 2014 at 15:55 Comment(1)
"You can substitute the type of L with any Type that has a logical shift left operator." You mean shift right operator.Circulate
A
1

You can instead of inputting the NUMBITS value as 8, input it as 2 (log2(8)), then retype as below to get around the problem, your generic just won't be as clean but it is scale-able.

entity BarrelShifter is

generic ( NUMBITS : integer :=2);                                                   
Port    ( INPUT     : in   std_logic_vector (((2**Nbits)-1) downto 0);                
          OUTPUT    : out  std_logic_vector (((2**Nbits)-1) downto 0);                
          SHIFT_CNT : in   std_logic_vector ((NUMBITS-1) downto 0)                 
        );                                                               
end BarrelShifter;
Autrey answered 21/2, 2017 at 13:10 Comment(1)
Keep in mind: log2(8) = 3Circulate

© 2022 - 2024 — McMap. All rights reserved.