Search code examples
vhdlhdl

How can I get the index of a one-hot encoded vector without using a for-loop?


I have a signal "event_id" where only one of the bits is high at any time. I need to convert this one-hot encoded signal to an integer-value of the index.

The signals:

signal event_id: std_logic_vector(3 downto 0);
signal event_index: natural;

I know I can do this with a for-loop. This is what I'm currently doing:

for i in 3 downto 0 loop
    if(event_id(i) = '1') then
      event_index = i;
    end if;
end loop;

Is there a better way to do this that doesn't require a for-loop or going through each bit individually? I know I could make a function with the for-loop method, but I feel like there should be a simple solution that I'm missing.


Solution

  • There is no "simple" solution for generating the index of a one-hot vector in VHDL.

    For a one-hot vector of 4 bits, thus a resulting index of 2 bits, the loop you have made is an OK solution, that is readable and does not take up too much resources in implementation. Though it is not the smallest solution, since it does not allow the implementation size to benefit from the one-hot property, as it returns the lowest index of a set bit. For short one-hot vectors this does not matter if the implementation is in an FPGA, since it uses quantized LUT resources.

    For longer one-hot vectors the implementation will be smaller if the one-hot property is used. This can be done with an algorithm where the index bits are generated from the one-hot vector and a mask. A function for this is shown below.

    -- One hot to index calculate; assuming LEN_HOT = 2 ** LEN_IDX
    function hot2idx_cal(hot : std_logic_vector) return std_logic_vector is
      variable mask_v : std_logic_vector(LEN_HOT - 1 downto 0);
      variable res_v  : std_logic_vector(LEN_IDX - 1 downto 0);
    begin
      for i in 0 to LEN_IDX - 1 loop
        -- Generate mask
        for j in 0 to LEN_HOT - 1 loop
          if ((j / (2 ** i)) mod 2) = 0 then
            mask_v(j) := '0';
          else
            mask_v(j) := '1';
          end if;
        end loop;
        -- Apply mask and generate bit in index
        if unsigned(hot and mask_v) = 0 then
          res_v(i) := '0';
        else
          res_v(i) := '1';
        end if;
      end loop;
      return res_v;
    end function;