Flux
Flux

Reputation: 419

How to determine if more than one bit in an STD_LOGIC_VECTOR is set in VHDL

I am wondering how I can determine if more than one bit of a four-bit STD_LOGIC_VECTOR is set to '1'.

e.g if it is "1001" or "1100" or "1111".

I am writing a program where I have to set an error signal to '1' if I get more than one control signal to my entity. The four control signals have been merged into one 4-bit STD_LOGIC_VECTOR and I need a smart way to determine if more than one of the bits are set.

Upvotes: 3

Views: 10019

Answers (5)

dieli
dieli

Reputation: 179

Basically you want to check if the vector is a power of two. Take a look at https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2

-- Taking the formula from the link above
-- f = (v & (v - 1)) == 0;
if (v and (v - 1)) > 0 then
   -- at least 2 bits are set, hence an error occured
end if;

I did not dry run the code (i guess some typecasts are missing), but it should scale pretty well.

Upvotes: 0

Giancarlo Sportelli
Giancarlo Sportelli

Reputation: 1297

The following function returns true when v has n bits set.

function is_nhot(v: std_logic_vector; n: natural) return boolean is
    variable ret : boolean;
    constant s   : std_logic_vector(v'length-1 downto 0) := v;
begin
    if n = 0 then
        ret := s = (s'length-1 downto 0 => '0');
    else
        if s'length < n then
            ret := false;
        elsif s'length = n then
            ret := s = (s'length-1 downto 0 => '1');
        else
            ret := ((s(s'length-1) = '1') and is_nhot(s(s'length-2 downto 0),n-1))
                or ((s(s'length-1) = '0') and is_nhot(s(s'length-2 downto 0),n));
        end if;
    end if;
    return ret;
end function;

The rationale is:

  1. If the input string s has less bits than n, it can't have n bits set
  2. If s is n bits long, they must all be set
  3. If s has more than n bits, perform the comparison recursively

To check if a std_logic_vector has 2 or more bits set, use

if not (is_nhot(v,0) or is_nhot(v,1)) then ...

Upvotes: 0

Morten Zilmer
Morten Zilmer

Reputation: 15924

Related to comments to fru1tbat answer, an alternative function for detection of more than one bit set can be:

function bits_set_two_or_more(v : std_logic_vector) return std_logic is
  variable one_or_more : std_logic := '0';
  variable two_or_more : std_logic := '0';
begin
  for i in v'range loop
    if one_or_more = '0' then
      one_or_more := v(i);
    else  -- one_or_more = '1'
      two_or_more := two_or_more or v(i);
    end if;
  end loop;
  return two_or_more;
end function;

Synthesis with Altera Quartus II (QII) to Cyclone V device with plenty of space, is shown in the "Or" column below, where "Add > 1" column is bits_set(v) > 1, and "N out" column is output from bits_set(v) to have a reference for how much reduction QII does when getting the expression bits_set(v) > 1.

enter image description here

The optimization for bits_set(v) > 1 is apparently a little bumpy for QII, as shown in "Add > 1" column around 16, but QII does actually use the bits_set(v) > 1 expression to reduce the logic instead of just doing a dumb compare.

Upvotes: 1

fru1tbat
fru1tbat

Reputation: 1625

Your vector is small enough that a brute-force solution is a reasonably simple way to go (you have fewer legal values than illegal values, so checking the legal values would be easier). You could also use a function (generalized here):

function bits_set(v : std_logic_vector) return natural is
  n : natural := 0;
begin
  for i in v'range loop
    if v(i) = '1' then
      n := n + 1;
    end if;
  end loop;
  return n;
end function bits_set;

Not sure how that will synthesize, but it should be sufficient. As noted below in the comments, for n=4, this actually appears to synthesize rather well in Quartus, at least. I would be curious to hear how other tools perform. As Morten says, it could have been coded more efficiently, especially for larger bit counts (so as a generalized solution, it's more illustrative than actually useful as-is, I suppose).

Upvotes: 2

Flux
Flux

Reputation: 419

I solved it with

with selvec select
ERR <=  '0' when "0001",
        '0' when "0010",
        '0' when "0100",
        '0' when "1000",
        '0' when "0000",
        '1' when others;

Not the neatest code but it does the trick.

Upvotes: 3

Related Questions